Changelog
All notable changes to pairmux are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.1.1 - 2026-07-20
Changed
- PyPI wheels now use a dedicated Markdown project description with copyable installation and quickstart commands, precise platform requirements, Agent Skill and MCP entry points, and links to the documentation, changelog, and issue tracker. Core Metadata also advertises tmux as an external requirement and adds discovery keywords; the release workflow rejects wheels whose description fails PyPI's strict renderer check.
- The README and documentation now use reproducible JSON examples, distinguish direct
.deb/.rpmdownloads from the not-yet-implemented APT repository, and document prompt detection, future-only pattern waits, program terminals, notes, and human handoff without overstating their guarantees. The note command's self-teaching hint now distinguishesrun/peeknote fields fromwait --human'soutputresult, and the nested-tmux attach hint no longer recommends an impossible cross-serverswitch-client.
0.1.0 - 2026-07-20
Fixed
- Tag releases now build the complete GoReleaser archive/package/checksum set once, verify and preserve those exact bytes with the four wheels, then promote them without recompilation. GitHub remains a draft until PyPI succeeds, tag commits and workflow syntax are checked, and concurrent runs serialize.
- The wheel builder recognizes GoReleaser's dotted arm64 variant directories such as
v8.0, validates both Mach-O architecture/deployment targets and ELF64 architecture, and rejects a binary whose native header does not match its wheel platform tag. Only canonical three-part SemVer tags are accepted; supported prerelease tags are normalized to PEP 440 wheel metadata. install.shnow stages the verified binary beside its destination and atomically renames it, replacing a pre-existing symlink instead of following it; it also requires the installed version to match the tag.- Unsigned and unnotarized Homebrew casks are no longer advertised or published. That channel remains deferred until macOS signing and a real Gatekeeper installation test are available.
- Terminal names and tmux socket labels are validated before any filesystem or tmux operation. State
now lives in an endpoint-specific hash of canonical
TMUX_TMPDIR, uid, and socket label, preventing traversal and cross-server metadata collisions while retaining an in-place, non-migrating fallback for the historical conventional default-socket layout. - Concurrent terminal creation uses a per-name reservation; journal preparation and command dispatch
now close their failure paths so a partial tmux operation cannot leak a pane or leave a permanent
E_BUSYcommand record. - Fish 4+ uses its native OSC 133 integration. Fish installations that emit no ready mark degrade to
the sentinel path using fish's
$status, rather than the POSIX-only$?expansion. wait --idlenow treats output silence as a prompt to refresh terminal state, not as proof that a command completed. Quiet running commands continue waiting; prompts and dead panes surface their actual status.- Managed-pane discovery now uses a printable tmux format separator. Alpine's C locale can sanitize control-character tabs into underscores, which previously made a newly created live pane look dead.
- Bounded
peekand default-logresponses now report skipped raw prefixes astruncated.omitted_bytesand provide the executablepairmux log NAME --range 1:endrecovery. Explicitlog --grep,--range, and--cmdselections read the complete requested history;--range A:endselects through the final shaped line. - Documentation builds override vulnerable transitive
serialize-javascriptanduuidreleases with patched versions;npm auditnow reports zero known vulnerabilities.
Added
pairmux mcp serveexposes the existing terminal commands as typed MCP2025-11-25stdio tools, preserving eachpairmux.v1envelope as structured and text content without invoking a wrapper shell.pairmux skill installembeds the canonical Agent Skill and installs it atomically for Claude Code, Codex, Gemini CLI, Cursor, OpenCode, GitHub Copilot, Windsurf, Kiro, Amp, or the cross-agentagentslocation. Codex andagentsshare the standard~/.agents/skillsdestination, and--target allupdates each existing agent directory only once.- Terminal lifecycle and core loop:
pairmux new(optional--name,--cwd,--cmd) opens a tmux-backed terminal;runblocks for command completion;peekreturns a bounded recent view;senddelivers interactive input;logreturns a bounded recent view or complete explicitly selected history with--cmd,--grep, and--range A:B/A:end;lslists status and activity; andkillends terminals while retaining their journals. waitfamily:pairmux wait <name>blocks until the shell is truly idle (--idle MS, the default), a regex matches new output (--pattern RE), or a human note arrives (--human), with--timeoutand a--notifydesktop ping.- Notes and human handoff:
pairmux note <name> <text>records a message that surfaces in the driving agent's nextrun/peekenvelope;wait --humaninstead returnshuman-donewith the note text inoutput. - Awaiting-input detection: a quiet command whose last screen line matches an interactive prompt
(
[y/N],password:, pager--More--/(END), "press any key") makesrunreturnawaiting-inputbefore its overall timeout. Password/passphrase/passcode prompts are classified as secrets and answered with a human-handoff hint rather than a guess. - Human-facing commands:
pairmux attach [name]takes over the live tmux session;pairmux watch [--interval 2s]renders a self-refreshing dashboard;pairmux doctorprobes tmux version, per-shell completion tier, state-dir writability and the notification backend. - Completion detection: injected OSC 133 integration for bash/zsh and native fish 4 marks are the
primary signal; a shell-specific sentinel marker is the fallback.
doctordistinguishes the diagnostichooks-no-Ctier from the stored terminal modes (hooksorsentinel). - Journal and locking: each terminal streams raw output through
tmux pipe-paneinto a per-terminal journal (raw.log+index.jsonl, mode 0600); reads are lock-free, writes take a per-terminal writerflockand return anE_BUSYenvelope naming the holder when contended. Attaching is a native tmux operation and records no event; humans leave an explicitnotewhen handing back. - Output shaping and
pairmux.v1JSON envelopes: every non-interactive command replies through a self-describing envelope (--json) carryingstatus, ANSI-stripped and carriage-return-collapsedoutput,truncatedrecovery metadata when output is elided,notes, orderednexthints, and stableerror.codevalues (E_NO_TERMINAL,E_EXISTS,E_BUSY,E_DEAD,E_BAD_ARGS,E_TMUX,E_INTERNAL).