Files
patterm/CHANGELOG.md
Harry Bayliss 83eb4f6b2d Add --version flag and enforce --long flags via pflag
Switches CLI flag parsing from Go's stdlib `flag` to spf13/pflag so
`--project` (and the internal `--socket` / `--identity` / `--scenario`
flags) are the only accepted form; single-hyphen long flags like
`-project` are now rejected. Help output renders the canonical `--`
form.

Adds `patterm --version`, which prints the build version, short commit,
and build date (e.g. `patterm v0.0.1 (commit abc1234, built 2026-05-14)`).
The version string is injected at build time — `make patterm` derives it
from `git describe --tags --always --dirty`, and the release workflow
injects the pushed tag. Commit/date come from the Go toolchain's
embedded VCS info via `runtime/debug.ReadBuildInfo`, so no manual
bumping is required.
2026-05-14 22:22:32 +01:00

220 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
All notable changes to patterm are tracked in this file. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
loosely follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [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.
### 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.
## [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.