Release v0.0.1
Some checks failed
release / build-linux-amd64 (push) Failing after 10m52s

Bundles the in-flight work into the first tagged release. See
CHANGELOG.md `[0.0.1] - 2026-05-14` for the full per-change list.
Highlights:

- Sidebar / chrome stability: clamp absolute cursor positioning and
  printable bytes to the viewport so long-running TUIs (claude, codex)
  can't spray into the right rail; bound tab bar's row clear to the
  viewport width so the rail isn't wiped on every tab redraw; flag
  scroll escapes (RI/IND/NEL/SU/SD/IL/DL) and clamp `CSI 0/1/2 J`/`K`
  to viewport columns.
- Palette: "Spawn process…" form, macros (`sw `, `k `, `sp `), kill
  entries mark the focused tab, dead agents drop out of the switch
  list.
- Sidebar: split into Processes (session-wide) + Agent Tree
  (per-active-agent) sections; relaunch indicator; Ctrl+W/S walks the
  combined list, Ctrl+A/D steps tabs.
- MCP: protocol handshake (`initialize`, `tools/list`, `tools/call`,
  `ping`), `mcp_injection.kind = cli_override / config_env` so codex
  and opencode pick up the server with no file writes, `lifecycle`
  help topic and tool-description cleanup-duty pointers.
- Lifecycle: orchestrator-spawned children cascade-killed when the
  parent dies; orchestrator-injected prompts end with CR + delayed
  Enter so claude submits cleanly.
This commit is contained in:
2026-05-14 22:04:32 +01:00
parent 63f0ddcb38
commit 52e06c914e
18 changed files with 1031 additions and 62 deletions

View File

@@ -16,12 +16,13 @@
},
{ "type": "wait_text", "contains": "RIBURST READY", "timeout_ms": 5000 },
{ "type": "wait_stable", "timeout_ms": 2000 },
{ "type": "assert_contains", "contains": "Session tree" },
{ "type": "assert_contains", "contains": "Processes" },
{ "type": "assert_contains", "contains": "Agent Tree" },
{ "type": "assert_contains", "contains": "● riburst" },
{ "type": "assert_contains", "contains": "Scratchpads" },
{
"type": "assert_regex",
"regex": "(?s)Session tree[^\\n]*\\n[^─\\n]*─[─]+[^\\n]*\\n[^●\\n]*● riburst",
"regex": "(?s)Processes[^\\n]*\\n[^─\\n]*─[─]+[^\\n]*\\n[^●\\n]*● riburst",
"timeout_ms": 2000
}
]

View File

@@ -0,0 +1,27 @@
{
"name": "sidebar_survives_wide_writes",
"cols": 120,
"rows": 40,
"scripts": [
{
"name": "widewrite",
"body": "#!/bin/sh\n# Reproduces a long-running TUI whose internal column state drifted\n# past the viewport width (the symptom seen in fucked-up-terminal.txt:\n# claude's input box drew a horizontal divider all the way to the host\n# edge, overwriting the sidebar). The widths here are: cols=120,\n# sidebarCols=28, so the viewport is 91 cols wide and the sidebar\n# border lives at col 92. Anything the child writes at col >= 92 lands\n# in the sidebar unless patterm defensively clamps it.\n#\n# After WIDE READY, emit 12 throw-away chunks to exhaust the focus\n# snapshot replay budget (8 chunks) so the wide-write clobber goes\n# through the *incremental* viewport renderer path. That's the path\n# that long-running sessions stay on — without clamping it can spray\n# bytes into the sidebar.\nprintf 'WIDE READY\\n'\ni=0\nwhile [ $i -lt 12 ]; do\n printf 'tick %d\\n' \"$i\"\n i=$((i + 1))\n sleep 0.05\ndone\nprintf 'PRE-CLOBBER\\n'\nsleep 0.2\nprintf '\\033[5;95HCLOBBER-CUP'\nsleep 0.1\nprintf '\\033[7;100HCLOBBER-CUP2'\nsleep 0.1\nprintf '\\033[9;1H'\nprintf '\\033[110GCLOBBER-CHA'\nprintf '\\nDONE\\n'\nsleep 5\n"
}
],
"steps": [
{
"type": "mcp_call",
"method": "spawn_process",
"params": { "kind": "command", "argv": ["widewrite"], "name": "widewrite" }
},
{ "type": "wait_text", "contains": "WIDE READY", "timeout_ms": 5000 },
{ "type": "wait_text", "contains": "DONE", "timeout_ms": 5000 },
{ "type": "wait_stable", "timeout_ms": 2000 },
{ "type": "assert_contains", "contains": "Agent Tree" },
{ "type": "assert_contains", "contains": "Processes" },
{ "type": "assert_contains", "contains": "Scratchpads" },
{ "type": "assert_contains", "contains": "● widewrite" },
{ "type": "assert_not_contains", "contains": "CLOBBER-CUP" },
{ "type": "assert_not_contains", "contains": "CLOBBER-CHA" }
]
}

View File

@@ -0,0 +1,31 @@
{
"name": "spawn_process_form",
"cols": 100,
"rows": 30,
"scripts": [
{
"name": "formfixture",
"body": "#!/bin/sh\necho FORM-READY\nsleep 5\n"
}
],
"steps": [
{ "type": "wait_stable", "timeout_ms": 3000 },
{ "type": "send_chord", "chord": "ctrl-k" },
{ "type": "send_text", "text": "Spawn process" },
{ "type": "send_chord", "chord": "enter" },
{ "type": "wait_text", "contains": "Spawn process", "timeout_ms": 3000 },
{ "type": "send_text", "text": "formfixture" },
{ "type": "send_chord", "chord": "tab" },
{ "type": "send_chord", "chord": "space" },
{ "type": "send_chord", "chord": "enter" },
{ "type": "wait_text", "contains": "FORM-READY", "timeout_ms": 5000 },
{
"type": "assert_mcp",
"method": "list_processes",
"path": "0.status",
"equals": "running"
},
{ "type": "assert_contains", "contains": "Processes" },
{ "type": "assert_contains", "contains": "⟳" }
]
}