Files
patterm/CHANGELOG.md

13 KiB
Raw Blame History

Changelog

All notable changes to patterm are tracked in this file. Format follows Keep a Changelog and the project loosely follows Semantic Versioning.

[Unreleased]

Added

  • patterm --version prints the build version, git commit, and build date (e.g. patterm v0.0.1 (commit abc1234, built 2026-05-14)). The version string is injected by the build (make patterm derives it from git describe; the release workflow injects the pushed tag). Commit and date come from the Go toolchain's embedded VCS info, so nothing has to be bumped by hand.
  • Ctrl+R restarts the focused command process from the Processes sidebar, including command entries that have already exited.

Changed

  • CLI flag parsing switched from Go's stdlib flag to spf13/pflag. --project (and the internal --socket / --identity / --scenario / --patterm-bin flags) are now the only accepted form — single-hyphen long flags like -project are rejected. Help output renders the canonical --flag form.

Fixed

  • Plain line-feed scrolling at the bottom of a child pane now invalidates and repaints the sidebar, so long agent output can no longer drag the sidebar border and labels out of view while the chrome cache stays warm.
  • Exited command processes in the top Processes section are now reachable with Ctrl+W/S navigation, so a dead shell entry can be focused and restarted instead of becoming a visible but unreachable row.

[0.0.1] - 2026-05-14

Fixed

  • Tab bar redraw used \x1b[2K to clear rows 1 and 2 before painting labels, which wiped the sidebar columns on those rows too. When the sidebar cache was still warm the rail never repainted, leaving a gap where the sidebar's top border and "Processes" header should be. The clear is now bounded to the viewport width.
  • Long-running TUIs (claude / codex) whose internal column state drifted past the patterm viewport could spray text into the sidebar columns — overwriting the session-tree and scratchpad rail until the user opened/closed the palette to force a full repaint. The viewport renderer now clamps absolute cursor positioning (CUP / HVP / CHA / HPA) to the viewport's right edge and drops printable bytes (ASCII and full UTF-8 glyphs) that would otherwise land past it. Covered by a unit suite and a new sidebar_survives_wide_writes harness scenario.
  • Sub-agent panes spawned while another diff-based TUI (claude/codex/ opencode) held focus could come up corrupted because the new child's first incremental updates targeted cells the host viewport hadn't populated yet. OnChildSpawned now primes the snapshot-replay budget for the new child, so its first PTY chunks render from the full styled emulator grid — the same path that fixed the symptom when the user manually cycled focus with Ctrl+W / Ctrl+S.

Changed

  • Command palette Kill … entries now mark the focused tab with the same "• … (current)" marker the Switch to … entries use, so the user can tell at a glance which tab a kill action targets.
  • Status line now advertises the navigation chords (Ctrl-A/D · tabs, Ctrl-W/S · tree) alongside Ctrl-K · palette. Hints decay shortest-first when the terminal is too narrow to fit all three.

Added

  • "Spawn process…" entry in the command palette opens a two-field form for typing an arbitrary command line and ticking "Relaunch on exit". The command runs through sh -lc so multi-word lines like bun run dev resolve binaries the way an interactive shell would. When the relaunch flag is set, patterm Starts the process again after it exits (1s backoff). Killing the process from the palette clears the flag so it does not come back.
  • Dedicated "Processes" section in the sidebar above the agent tree, listing every top-level command/terminal process. It is global to the patterm session — switching between agent tabs no longer changes which processes are visible. The relaunch-on-exit indicator () shows next to processes the user opted into auto-restart for.
  • Ctrl+W / Ctrl+S now traverse the combined Processes section and the active agent tree as one flat list, so the user can step out of the agent tree into the Processes pane and back without leaving the keyboard.
  • New lifecycle help topic spelling out that the caller owns the processes it spawns and should call close_process when a sub-agent or spawned process is no longer needed. The spawn_agent and spawn_process tool descriptions advertised via tools/list now call out the same cleanup duty (with a pointer to help('lifecycle')), and the spawning help topic and topics index cross-reference it.
  • spawn_agent now prepends a single-line [system: …] orientation block to the sub-agent's first prompt so vendor TUIs (claude, codex, opencode) know they're a sub-agent, are reminded to send_message their parent with a summary when done, and to clean up processes / scratchpads they created. Skipped for top-level launches and empty instructions.
  • Ctrl+A / Ctrl+D step focus between top-level tabs; Ctrl+W / Ctrl+S step through processes (root + sub-agents) inside the current tab. Recognised in legacy, kitty CSI u, and xterm modifyOtherKeys encodings. The chord shadows the corresponding raw byte for the focused pane — pressing Ctrl+D no longer sends EOF to the underlying shell, for instance.
  • MCP protocol layer (internal/mcp/protocol.go) implementing initialize, tools/list, tools/call, ping, and MCP notifications. Tool catalog with input schemas is advertised via tools/list. Real MCP clients (claude, etc.) can now complete the handshake against patterm's per-PID socket. Legacy direct-tool dispatch is preserved so the harness keeps working unchanged.
  • mcp_injection.kind = "cli_override" for agents that accept inline key=value config overrides on the command line. The default codex preset uses it to emit -c mcp_servers.patterm.command=… and -c mcp_servers.patterm.args=[…] — zero files written, no CODEX_HOME override.
  • mcp_injection.kind = "config_env" for agents that read their config from an env var. The default opencode preset uses it to pass a merged opencode.json inline through OPENCODE_CONFIG_CONTENT, so auth/agents/tui.json/skills resolve from the user's real $HOME with no XDG override.
  • Palette macros: typing sw , k , or sp filters the list to switch / kill / spawn entries respectively. Footer shows the available macros.

Changed

  • The sidebar's session-tree section is now labeled "Agent Tree" and shows only agent sessions (and any sub-agents they spawn). Top-level command and terminal processes live in the new "Processes" section above it.
  • Tab bar tabs now correspond to agent sessions only. Command/terminal processes that previously claimed a top-level tab now appear in the Processes sidebar section, so the tab strip is reserved for agent context.
  • Focus, lifecycle, and repaint paths now capture terminal layout before taking UI state locks, reducing resize-time deadlock risk without changing visible behavior.
  • Focused PTY output no longer rebuilds the scratchpad sidebar on every chunk. The sidebar still repaints on focus/lifecycle/resize changes and when child output scrolls over the chrome, but normal output avoids repeated scratchpad disk reads.
  • Harness scenario tests now reuse one built patterm binary per test run and write failure artifacts under a repo-rooted, collision-proof directory.
  • Palette ordering: open agents/processes (Switch to …) now appear above the option to spawn new ones, with kill entries pushed down toward the end of the list.
  • Tab bar trimmed from three rows (label / subtitle / underline) to two (label / underline). Tabs flex to fill the available host width evenly with leftover columns distributed to the leftmost tabs; the + new hint sits in a reserved slot on the right. Layout's mainTop consequently drops from 4 to 3, giving each pane one extra row of viewport.

Fixed

  • Agent MCP injection no longer writes unused config files for inline injection modes (cli_override / config_env). File-backed injection modes track their generated paths and clean them up when the child is closed, exits, or patterm shuts down.
  • MCP tools/list descriptions now match the runtime argument values for process output and pattern waiting, and typed invalid-argument errors map to JSON-RPC invalid params instead of generic internal errors.
  • Scratchpad writes and appends are serialized inside a patterm process so expected_revision checks cannot race another local scratchpad mutation.
  • The sidebar scratchpad list now refreshes after MCP scratchpad_write and scratchpad_append calls.
  • UI chrome now reads renamed child display names through the DisplayName accessor, avoiding races with rename_process.
  • Child processes spawned by an orchestrator are now killed when the orchestrator dies, recursively through the tree. Applies whether the parent was closed via MCP, Ctrl-C'd by the user, or exited on its own — reapChild cascades a SIGTERM (escalating to SIGKILL after 2s) to every direct child, and each descendant's own reap fires the same cascade so the kill flows through arbitrary depth.
  • Killed agents no longer linger in the command palette. Agent entries that aren't running are filtered out of the switch list; session-persistent commands (which can be restarted) stay visible.
  • tools/list now emits a concrete properties object ({}) for parameterless tools instead of null. Claude rejected the null-properties form with "tools fetch failed" even though the initialize handshake had succeeded.
  • Sidebar no longer flickers on every PTY chunk. The tab bar, sidebar, and status line now cache their last rendered byte string and skip the write when the new frame matches; full repaint paths (resize / focus change / palette close / screen clear) invalidate the cache so the next draw fires unconditionally.
  • Spawning a child agent now clears the viewport area before it paints, so the previous focused child's PTY output no longer bleeds through underneath the new pane.
  • Orchestrator-injected input (initial agent prompts, MCP send_input with submit: true, send_message, timer_wait callbacks) now ends with CR (\r) instead of LF (\n). Claude treated \n as "newline in textarea"; with CR the prompt actually submits, matching what the host terminal sends when a user presses Enter directly.
  • Enter is now written to a child PTY as its own write() call, separated from the preceding text by a short delay. Both InjectAsUser (user typing forwarded through patterm) and InjectAsOrchestrator (MCP / send_message / initial-prompt paths) share the split. Without it, claude — and other paste-detecting TUIs — coalesced "hello\r" into one read and inserted the CR as literal text instead of treating it as the Enter keystroke.
  • Sidebar (and tab bar) no longer get wiped when the focused child issues CSI 0 J / CSI 1 J (clear-to-cursor). The viewport renderer already clamped CSI 2 J and CSI K to viewport columns, but the partial-screen variants were forwarded verbatim, so any tool-call expansion in claude (Ctrl+O) would erase every cell to the right of the cursor — including the right rail. Both forms are now translated into per-row ECH sequences that stop at the viewport's right edge.
  • Sidebar left border no longer vanishes when the viewport repaints. The border column was the same column as the viewport's rightmost cell, so any child write to that column (or clearViewport's ECH) would erase it. The viewport is now one column narrower so the border has a dedicated column.
  • Sidebar session-tree entries no longer get pushed downward when an agent emits a scroll burst on startup. Codex (Ratatui) issues 8× RI (\x1bM) right after setting its scroll region, which scrolls the region down across every column — dragging the right-rail entries with it. The chrome cache then hid the clobber because the computed frame still matched. The viewport renderer now flags any chunk that contains a scroll-triggering escape (RI / IND / NEL / SU / SD / IL / DL) and OnPTYOut drops the sidebar cache when the flag is set, so the next drawSidebar repaints over the drift.

Conventions

  • This file is the single record of user-visible changes; the TODO is scratch space, not history.
  • One bullet per change, written in the past tense from the user's point of view. Reference the package or preset name when it helps a reader find the code.