This batches the in-flight [Unreleased] block from CHANGELOG.md into a single commit. Highlights: - Real MCP protocol layer (initialize / tools/list / tools/call) so vendor MCP clients can complete the handshake against the per-PID socket. Legacy direct-dispatch preserved for the harness. - New mcp_injection kinds — cli_override for codex, config_env for opencode — joining the existing env-var and config_file paths so patterm can slot into more agents without touching their real config or auth. - Ctrl+A/D and Ctrl+W/S focus navigation across tabs and intra-tab process lists, recognised in legacy / kitty CSI u / xterm modifyOtherKeys encodings. - Palette macros (sw / k / sp ) and reordering so open sessions surface above spawn-new entries. - Two-row tab bar, sidebar/tabbar/status chrome cache, viewport-wipe on agent spawn, CR-terminated orchestrator injections, and split- Enter PTY writes so paste-detecting TUIs see Enter as a key event. Also fixes the bug logged in TODO: claude's Ctrl+O tool-call expansion emits CSI 0 J, which the viewport renderer was forwarding verbatim — wiping the sidebar to the right of the cursor and leaving the chrome cache convinced nothing had changed. CSI 0 J and CSI 1 J are now translated into per-row ECH sequences clamped to the viewport, same as CSI 2 J and CSI K already were. Agent guides (CLAUDE.md / AGENTS.md) now spell out the TODO->CHANGELOG workflow so completed items land in the changelog rather than as ticked entries left behind in TODO.
4.7 KiB
Agent Guide
Project Overview
patterm is a Go terminal orchestration TUI. It runs as one foreground process that owns:
- the host TUI chrome
- child PTYs
- headless
libghostty-vtemulators for rendered-grid snapshots - the in-process MCP server
- per-project scratchpads and command-preset trust state
There is no daemon or attach/detach model. Closing the patterm process tears down every child process it spawned.
Core packages:
cmd/patterm: main binary, includingmcp-stdioanddebug-harnesssubcommands.internal/app: TUI/session state, palette, child lifecycle, rendering, MCP tool host.internal/pty: thin wrapper aroundgithub.com/creack/pty.internal/vt:libghostty-vtbacked emulator wrapper.internal/mcp: JSON-RPC MCP socket server and tool surface.internal/preset: XDG-loaded agent/process presets.internal/trust: per-project command-preset trust grants.internal/harness: black-box PTY/MCP test harness.
Build Prerequisites
The normal build depends on the vendored libghostty-vt static library and headers under third_party/libghostty-vt/install.
If that install tree is missing, run:
make deps
This fetches the pinned Ghostty commit and builds libghostty-vt with Zig. The Makefile currently requires zig on PATH with a compatible version.
Common checks:
go test ./...
go build -o ./bin/patterm ./cmd/patterm
Harness Testing
The harness is the preferred way to test patterm end-to-end without a human watching a terminal.
It starts a real patterm child process in a PTY, feeds its output through the same internal/vt emulator used for child panes, and talks to the child patterm process over its per-PID MCP Unix socket. Scenarios are JSON files under:
internal/harness/scenarios/
Useful harness commands:
go test ./internal/harness/...
go test -race ./internal/harness/...
go test -count=10 ./internal/harness/...
Run one scenario through the CLI:
go build -o ./bin/patterm ./cmd/patterm
./bin/patterm debug-harness --scenario internal/harness/scenarios/spawn_process_via_palette.json
Harness scenarios create hermetic XDG/config/data/runtime directories, write scenario-local presets and fake scripts, and set PATTERM_HARNESS=1. They must not read or write the user's real patterm config.
Failure artifacts are written under:
internal/harness/.artifacts/
That directory is gitignored. Artifacts include rendered grid text, raw PTY bytes, serialized VT state, MCP snapshots, the resolved environment, and an annotated scenario.
Harness Environment Notes
The harness and patterm MCP server create Unix sockets and spawn PTYs. In restricted sandboxes this can fail with errors such as:
listen unix ... setsockopt: operation not permitted
When that happens, rerun the harness tests or debug-harness command in an environment that permits Unix sockets and PTYs.
When testing a specific binary, set:
PATTERM_BIN=/absolute/path/to/patterm go test ./internal/harness/...
Without PATTERM_BIN, harness tests build the current checkout once into a temp location and test that binary.
Changelog
User-visible changes go in CHANGELOG.md (Keep-a-Changelog format).
When finishing work that affects users — new MCP tools, palette
behavior, preset shapes, host chrome, anything observable — add a
bullet under [Unreleased] in the appropriate Added / Changed /
Fixed / Removed section. The TODO file is scratch space, not
history; the changelog is the record.
When a TODO.md item is actioned (bug fixed, behavior changed,
feature shipped), the resolution belongs in CHANGELOG.md — not as
a "done" entry left in TODO.md. Workflow:
- Land the code change.
- Add a
[Unreleased]bullet inCHANGELOG.mddescribing what the user will now experience differently. - Remove the corresponding item from
TODO.md(don't tick it off and leave it behind —TODO.mdonly lists outstanding work).
If a TODO item turns out to be a non-issue or gets dropped without a
code change, just delete it from TODO.md; no changelog entry is
needed.
Development Notes
- Prefer existing package boundaries. MCP protocol shapes live in
internal/mcp; runtime behavior usually belongs ininternal/app. - Keep terminal rendering changes covered by focused app tests or a harness scenario.
- Do not let child PTY output own host chrome. The app owns tab bar, sidebar, status line, and palette.
- Command-preset trust must be seeded before starting patterm in tests because the app opens the trust store during startup.
- If a scenario needs external CLIs such as
claude,codex, oropencode, gate it behind an explicit opt-in environment variable. CI scenarios should use fake scripts/presets.