Typed tmux operation chains#81
Open
tony wants to merge 15 commits into
Open
Conversation
why: Build the experimental chain-command MCP tools against the in-progress
libtmux._experimental.chain API on the sibling libtmux worktree.
what:
- Add [tool.uv.sources] libtmux = { path = "../libtmux", editable = true }
- Relock against the local editable checkout
why: Agents needed to run several tmux commands as one native invocation
instead of one tool call per command.
what:
- Add run_command_chain: a list of {command, args, target} folded into one
`tmux a ; b` dispatch via libtmux._experimental.chain (CommandChain.run,
run off the event loop with asyncio.to_thread)
- Destructive tier; refuse kill-server; fail closed on an empty list/target
- Add ChainCommand / RunCommandChainResult models; register the tool
- Tests: one-dispatch effect, atomic abort, validation, kill-server denial
why: A single tmux `;` chain can't hand back the ids it creates (a fresh id can't be substituted into the same invocation), so callers had no way to split a pane and learn the new pane ids. what: - Add build_forward_layout: split a seed pane N ways and return each new pane id, resolved over the minimum dispatches via ForwardPlan and AsyncServerPlanRunner (off the event loop) - Optional per-split shell / send_keys; mutating tier (reaches a shell) - Add ForwardSplit / ForwardLayoutResult models; register the tool - Tests: two splits capture distinct ids, single-split fold + send_keys lands, empty-list validation
why: FastMCP log capture can surface the same child logger event through both direct and parent propagation paths, which made the level check brittle. what: - Assert the set of matching fastmcp.errors levels - Keep warning/error demotion coverage for tool errors
why: CI cannot install a sibling worktree path, so the MCP branch needs a public immutable libtmux source for the experimental chain API. what: - Replace the editable sibling path with the published libtmux commit - Regenerate uv.lock with the Git source
why: Raw tmux command chains forced callers to choose between one native dispatch and typed, per-operation results. A typed compiler fills that gap while keeping output and continue-on-error semantics honest. what: - Replace raw chain/layout tools with run_tmux_operations - Add discriminated operation models and structured step results - Fold chainable runs and split standalone output/id captures - Document the new chain tool surface
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #81 +/- ##
==========================================
- Coverage 84.67% 83.65% -1.03%
==========================================
Files 43 44 +1
Lines 3197 3670 +473
Branches 438 515 +77
==========================================
+ Hits 2707 3070 +363
- Misses 360 436 +76
- Partials 130 164 +34 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
why: A split that immediately feeds typed decorations should keep the
one-dispatch behavior that tmux supports through the marked-pane target while
still returning the created pane id.
what:
- Detect immediate send_keys/resize operations targeting a fresh split ref
- Compile them through tmux's {marked} target in one sequence
- Assert the single-dispatch split-ref path in tests and docs
why: The typed operation compiler is now part of this branch's public surface, but the unreleased notes and tool page did not describe what callers can rely on. what: - Add an unreleased changelog entry for run_tmux_operations - Document dispatch boundaries and the generic batch-tool boundary
why: The MCP operation compiler was duplicating libtmux's chainability and scope contract, which let the two surfaces drift as new operations were added. what: - Validate lowered commands with libtmux chain metadata - Report contract failures as operation-level compile failures - Add an exhaustiveness assertion for typed operation lowering - Cover contract drift with focused tests
why: Agents need to inspect the native tmux dispatches a typed operation list would produce before mutating tmux. what: - Add dry_run to run_tmux_operations and result models - Return planned step and dispatch results with nullable exit codes - Use deterministic placeholders for dry-run split pane refs - Document dry-run behavior and add regression tests
why: Planned dry-run steps should not stop later operations when the compiler flushes a pending dispatch before an output step. what: - Treat planned dry-run steps as successful for control flow - Reuse the same success predicate for final results - Add a regression covering dry-run output-step continuation
why: Native tmux chains can block the MCP call when a dispatch stalls, so callers need a typed failure instead of an unbounded await. what: - Add dispatch_timeout validation to run_tmux_operations - Mark timed-out dispatches and included steps as failed - Cover chain, standalone, and marked split timeout paths - Document the timeout behavior and background worker caveat
why: The typed chain compiler has branch-local failure paths for refs, pending flushes, and marked split chains that need explicit coverage. what: - Cover unknown pane_ref compile failures - Cover compile errors after a failed pending dispatch - Cover marked split failure skipping later operations
why: A typed operation list can create panes before a later step fails, leaving partial layout state behind for callers that need all-or-nothing behavior. what: - Add rollback_on_error to run_tmux_operations - Kill created split-ref panes in reverse order on failure - Report rolled_back_panes and rollback_errors in results - Document rollback behavior and cover enabled and disabled cases
why: The MCP chain tools need the pushed libtmux chain control-mode surface that preserves per-command results. what: - Update the libtmux git pin to 6fc3db63 - Refresh uv.lock for the new pinned revision
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
run_command_chainand narrowbuild_forward_layouttools with one typedrun_tmux_operationstool.split_pane,send_keys,resize_pane,select_layout,set_option, andcapture_pane, plus structured per-step and per-dispatch results.tmux a ; b ; c, output/id capture steps stay attributable, andon_error="continue"uses standalone dispatches because native tmux chains abort on first failure.split_panecan fold with immediatesend_keys/resize_paneoperations that target itspane_refthrough tmux's{marked}target while still returning the concrete pane id.Changes by area
src/libtmux_mcp/tools/chain_tools.pyAdds the typed compiler and registers
run_tmux_operationsas a mutating, open-world tool. The compiler keeps a pending chain of no-output operations, flushes before output reads, captures split ids when needed, and returns both rendered argv and per-step status.src/libtmux_mcp/models.pyAdds the typed operation union and structured result models. The operation list is validated through a module-level Pydantic
TypeAdapterin the tool module.Docs
Adds chain-tool docs and wires
chain_toolsplus the new models into the FastMCP docs catalog.Dependency pin
Updates
[tool.uv.sources]anduv.lockto use the public libtmux branch commit591a312f78d165816bb95a035a46219657c4b53d.Design notes
kill-serverare not operation variants.capture_paneand id-producing split steps force a dispatch boundary unless the single split-ref{marked}optimization applies.on_error="continue"deliberately disables native chaining. tmux;sequences have abort-on-first-error semantics, so continuing later operations requires separate dispatches.Test plan
rm -rf docs/_builduv run ruff check . --fix --show-fixesuv run ruff format .uv run mypy .uv run py.test --reruns 0 -vvv(612 passed)just build-docsCompanion PR
Depends on the libtmux experimental chain API in tmux-python/libtmux#685.