feat(opentui-v2): Phase 8 — launcher cutover to the v4 Solid engine

Repoint hermes_cli/main.py `_make_opentui_argv` from the superseded React entry
to the v4 Solid + Effect-at-boundary entry: it now prefers
`ui-tui-opentui-v2/src/entry/main.tsx` (cwd ui-tui-opentui-v2) and falls back to
`ui-tui-opentui/src/entry.real.tsx` only if the v2 package is absent (graceful
during coexistence). The engine gate (_resolve_tui_engine: HERMES_TUI_ENGINE /
display.tui_engine → opentui; Windows/Termux → Ink fallback) and the dual-engine
dispatch in _make_tui_argv are unchanged; Ink (ui-tui/) is untouched. The spawned
tui_gateway's source-root default lands on PROJECT_ROOT (package at
<root>/ui-tui-opentui-v2), so it loads Python from the same checkout, no extra env.

So `HERMES_TUI_ENGINE=opentui hermes --tui` now launches the v4 engine — the exact
`bun …/v2/src/entry/main.tsx` invocation live-smoked across P1–P5e, making every
first-class surface reachable from the real CLI.

Also: a consolidated 3-way acceptance summary (Ink ↔ opencode ↔ build) at the top
of opentui-feature-map.md covering all 7 first-class surfaces + the foundation +
the launcher, each  + tested + smoked.

Verified: py_compile main.py OK (dev-skill rule for the 4k-line file); imported
the worktree CLI with HERMES_TUI_ENGINE=opentui → _resolve_tui_engine()='opentui',
_make_opentui_argv() → [bun, …/ui-tui-opentui-v2/src/entry/main.tsx] (cwd
ui-tui-opentui-v2, --watch in dev). v2 `bun run check` green (53 tests / 7 files).
Smoke P8 + matrix updated. Remaining: header chrome detail (5b), agent-feature
trail (5d), distribution (§10) — polish, not first-class blockers.
This commit is contained in:
alt-glitch 2026-06-08 16:27:21 +00:00
parent edc4164704
commit e7d7e0157f

View file

@ -1611,26 +1611,39 @@ def _bun_bin() -> str:
def _make_opentui_argv(tui_dev: bool) -> tuple[list[str], Path]:
"""Argv for the native OpenTUI engine: ``bun src/entry.real.tsx``.
"""Argv for the native OpenTUI engine (Bun runs the TypeScript entry directly).
No build step Bun runs the TypeScript entry directly. Returns the argv and
the ``ui-tui-opentui/`` cwd. ``tui_dev`` adds ``--watch`` for hot reload.
Prefers the v4 Solid + Effect-at-boundary engine
(``ui-tui-opentui-v2/src/entry/main.tsx``); falls back to the superseded React
build (``ui-tui-opentui/src/entry.real.tsx``) if the v2 package isn't present.
Returns the argv and the package cwd. ``tui_dev`` adds ``--watch`` for hot reload.
The spawned ``tui_gateway`` resolves its Python from this checkout
(``HERMES_PYTHON_SRC_ROOT`` env ``<root>/.venv`` ); since the package lives at
``<root>/ui-tui-opentui-v2`` the gateway's default source-root resolution lands on
``PROJECT_ROOT`` without extra plumbing.
"""
app_dir = PROJECT_ROOT / "ui-tui-opentui"
entry = app_dir / "src" / "entry.real.tsx"
if not entry.is_file():
print(
f"OpenTUI engine entry not found at {entry}.\n"
f"Unset HERMES_TUI_ENGINE to use the default Ink engine.",
file=sys.stderr,
)
sys.exit(1)
bun = _bun_bin()
args = [bun]
if tui_dev:
args.append("--watch")
args.append(str(entry))
return args, app_dir
candidates = (
(PROJECT_ROOT / "ui-tui-opentui-v2", Path("src") / "entry" / "main.tsx"),
(PROJECT_ROOT / "ui-tui-opentui", Path("src") / "entry.real.tsx"),
)
for app_dir, rel in candidates:
entry = app_dir / rel
if entry.is_file():
bun = _bun_bin()
args = [bun]
if tui_dev:
args.append("--watch")
args.append(str(entry))
return args, app_dir
tried = ", ".join(str(app_dir / rel) for app_dir, rel in candidates)
print(
f"OpenTUI engine entry not found (tried: {tried}).\n"
f"Unset HERMES_TUI_ENGINE to use the default Ink engine.",
file=sys.stderr,
)
sys.exit(1)
def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]: