Skip to content

v2.3.0

Choose a tag to compare

@lucascaro lucascaro released this 19 Jul 20:05
· 371 commits to main since this release

Added

  • Agents: define your own tools. A new Settings screen (⌘, on macOS, also
    under File ▸ Settings… and in the command palette; Ctrl+, or the
    command palette elsewhere) lets you add custom
    agents with a name, command line, and color — for example a claude-lite
    that runs claude --model haiku. Custom agents appear in the ⌘T launcher
    alongside the built-ins, with the same PATH availability check, and persist
    and revive across restarts. Definitions live in agents.json in the Hive
    state directory and can be hand-edited. Built-in agent ids cannot be
    redefined, and custom agents do not support conversation resume — Restart
    re-runs their command rather than continuing the prior session.

  • GUI: ⌘B jumps to the next session that rang the bell (the pulsing
    ones), cycling through them and wrapping — no more hunting for the
    flashing row with the mouse. ⇧⌘B jumps back to the session you were
    working in before the first ⌘B — so a round of bells can bounce you
    through several sessions and one keystroke still returns you to the
    work you interrupted. A minimized session that rings its bell is
    restored on the way in and returned to the tray on the way back. Both are in the ⌘/ help overlay, the
    command palette, and the macOS Session menu (Ctrl+B / Ctrl+Shift+B
    on Windows and Linux).

  • Agents: Pi (@earendil-works/pi-coding-agent) is now
    a launchable agent. Hive pins each session's id via pi --session-id, so
    Restart resumes the exact conversation by id — unambiguous even when
    sibling sessions share a worktree (same guarantee as Claude/Gemini).

  • GUI: keyboard-shortcuts help overlay on ⌘/, listing every binding —
    including the ones no menu shows (terminal-level Ctrl+Shift+C/V/A
    copy/paste/select-all, ⇧↩ newline, launcher digit keys, sidebar
    resizer keys). Also reachable from the command palette ("Keyboard
    Shortcuts") and the macOS Help menu. The palette's shortcut column
    and the overlay render from one shared table (lib/shortcuts.js)
    so they cannot drift, and non-mac platforms now see Ctrl+-style
    hints instead of mac glyphs.

  • GUI: actionable empty states. First run shows "No sessions yet" with
    a New session button; an empty project and an all-minimized grid get
    their own hints — replacing the bare CSS placeholder text (or a
    fully blank pane in single mode).

  • GUI: sidebar project collapse state persists across launches
    (localStorage, hive.collapsedProjects; pruned when projects are
    deleted).

  • GUI: the agent launcher opens instantly with a "Loading agents…" row
    (then "No agents found" when the list is empty) instead of popping
    in fully-formed a beat after the keypress; creating a session
    flashes "creating session…" in the status bar.

  • GUI: accessibility pass — the sidebar footer hint no longer claims
    the palette is on ⌘K (it is ⇧⌘K); project collapse carets are real
    buttons with aria-expanded and keyboard focus rings; the dead
    session overlay is an alertdialog; palette input, tile headers,
    and tray chips carry aria-labels; keyboard-focus rings on sidebar,
    tile, tray, and banner controls.

  • GUI: minimize sessions to hide them from grid views. Each tile in
    the project-grid and all-sessions-grid now has a small minimize
    button on the right of its header (–). Minimized sessions are
    removed from the grid and appear as chips in a tray below the
    terminals; clicking a chip restores the session to the grid and
    switches to it. Minimized sessions stay alive (output keeps
    buffering) and remain reachable in single-session mode via the
    sidebar, command palette, and ⌘[/] cycling. State is per-app-run
    (not persisted across launches). (#202)

  • Frontend test harness. scripts/test.sh runs four layers: Go
    (existing), Vitest unit tests for the new cmd/hivegui/frontend/src/lib/
    pure modules, jsdom DOM tests, and a Playwright E2E suite that
    drives the GUI through an in-browser Wails-mock bridge
    (test/e2e/wails-mock.js). Grid layout math (computeGridDims,
    buildGridLayout, computeSpatialMove), the visibility gate for
    xterm fit(), the snake/camel wire normalizer, and the
    cross-platform cmdOrCtrl helper are now covered as pure
    functions — the regression classes behind the recent ctrl-arrow,
    grid-mode-revert, and canvas-resize bugs all have direct unit
    tests. Linux CI runs every layer on every PR.

  • Daemon integration tests covering update/restart/resize and
    multi-control-conn broadcast fan-out.

  • GUI: last view (single / grid-project / grid-all) persists across
    launches. Previously the GUI always booted into single-focus mode
    regardless of how it was left; now it restores the mode you last
    used. Stored in localStorage under hive.view. (#187)

Changed

  • GUI: Ctrl+? now opens the keyboard-shortcuts panel on Windows and
    Linux, alongside the existing Ctrl+/. macOS already accepted both
    ⌘? and ⌘/ via the Help menu item; those platforms have no native
    menu, so only Ctrl+/ worked there.
  • GUI: the sidebar footer now shows the running version and build of
    hive and hived instead of the keyboard-shortcut hints. It shows a
    single line when both binaries come from the same build, and expands
    to one line each — highlighted — when they differ, making a stale
    daemon visible at a glance rather than only via the restart banner.
    The shortcut hints are still available via the command palette
    (⇧⌘K) and the keyboard-shortcuts overlay (⌘/).
  • GUI: the Debug menu is relabeled "Toggle Debug Trace" / "Copy Debug
    Trace" (was "Scroll Debug" / "Scroll Trace"); the captured trace now
    covers grid relayout, focus, keydown routing, a main-thread heartbeat,
    and inbound daemon traffic — not just scrolling.

Fixed

  • GUI: "Restart Hive" now actually replaces the daemon — or says why it
    can't. It asks hived to exit over the control connection Hive
    already holds, then confirms the socket has gone quiet before
    relaunching, so the new window can no longer come back attached to
    the daemon it was supposed to replace (the stale-build banner
    returning immediately after a "restart" was this bug). Signalling the
    pid recorded in <sock>.pid remains as a fallback, but the pidfile is
    no longer trusted as proof: three of its paths could report success
    having killed nothing. If the daemon survives both attempts, the
    restart fails visibly in the banner instead of quitting into a broken
    state. Restarts are also no longer stalled ~5s by a liveness check
    that could never observe the daemon exiting.

  • GUI: hived exiting no longer deletes a newer daemon's pidfile,
    which left Restart Hive with no handle on the running daemon at all.

  • GUI: Restart Hive… is now in the File menu and the command
    palette. It was previously reachable only from the daemon-stale
    banner, so when the GUI and daemon builds matched there was no way to
    restart Hive from inside the app.

  • GUI: errors are written to hivegui.log in the Hive state directory,
    next to hived.log. Under LaunchServices the GUI's stderr goes to
    /dev/null, so failures previously left no trace anywhere on disk.

  • GUI: renaming a session from its tile header no longer throws a
    ReferenceError and silently discards the new name. The tile
    rename's commit handler called UpdateSession without it being
    imported, so the rename input closed but the daemon call never
    fired.

  • GUI: full-screen agent sessions (claude/codex/pi and other alternate-
    screen TUIs) no longer freeze for seconds on resize or a single↔grid
    toggle. Such a resize requested a full scrollback replay — the daemon
    re-streams its entire raw byte ring (toward an 8 MB cap on a long-lived
    session) and the GUI parsed it in one synchronous write — but the
    alternate screen has no user-facing scrollback and the program repaints
    itself from SIGWINCH, so the replay was pure cost. It is now skipped on
    the alternate screen; normal-buffer (shell) sessions still replay.

  • GUI: scrollback replays no longer destroy the reading position
    ("scrolling jumps around with codex"). When a window resize, sidebar
    drag, or grid reflow triggered a scrollback replay while the user
    was scrolled up reading history, the replay's buffer reset dumped
    them at the bottom — the #213 don't-snap flag only suppressed the
    final explicit snap, after the position was already gone. The replay
    now captures the reader's distance from the bottom before the wipe
    and restores it once the replay has fully parsed. The reset and the
    final viewport placement are also parse-ordered now (xterm's write
    queue is async), so under codex-rate output a replay can no longer
    interleave with unparsed live bytes and paint duplicated lines.
    Deliberate mode switches still snap to the bottom. Adds a scroll
    tracer (localStorage hive.debug = '1', dump
    window.__hive_scrolltrace) for field diagnosis, and a real-daemon
    e2e suite (scroll-codex.spec.js) that reproduces the bug on the
    old code and locks the invariants.

  • GUI: action failures are no longer silent. Copying/pasting inside a
    terminal, creating/duplicating/restarting/closing a session, renaming
    or recoloring or reordering sessions and projects, saving a project,
    and opening the agent launcher now show an error in the status bar
    when the underlying daemon call fails (e.g. connection lost) —
    previously the click just did nothing, with no trace. Status-bar
    errors are guaranteed a minimum visibility window (1.5s), pulse red,
    auto-hide (errors 6s, info 2.5s), revert to the persistent status
    ("connected" / session name) instead of going blank, and are
    announced to screen readers (role="status", aria-live="polite").
    Also fixed: opening the launcher when its sidebar anchor row isn't in
    the DOM could throw and leave the launcher unopened with no feedback.

  • GUI: Enter now commits and Escape now cancels inline renames (session
    rows, project rows, and tile headers). A capture-phase
    stopPropagation() listener on the rename input was cancelling the
    same input's bubble-phase Enter/Escape handler (DOM dispatch skips
    the bubble invocation once the stop-propagation flag is set), so
    Enter did nothing and Escape couldn't cancel — the rename only ever
    committed when the input lost focus, including after an attempted
    Escape. The handlers now live in the single capture-phase listener.

  • Daemon diagnosability hardening: failures that were previously
    swallowed are now logged. Registry metadata-persist failures (12
    sites — session/project entries and order indexes) warn with the
    operation that lost the write; dropping a slow session/project event
    listener warns instead of silently desyncing the GUI (and the event
    buffers grew 16→64 so a many-session reorder can't overflow a
    healthy listener); pidfile cleanup failures are logged at shutdown.
    Worktree creation failures now report every attempted strategy
    (joined errors instead of last-attempt-only), and existing-branch
    detection uses git rev-parse exit codes instead of matching git's
    locale-dependent error text. The hived-ws-bridge test shim rejects
    malformed JSON-RPC params instead of running handlers on zeroed
    structs, and serializes concurrent frame writes per daemon
    connection so racing writers can't corrupt the wire stream.

  • GUI: keystrokes typed immediately after switching to a grid view are no
    longer dropped. Switching to grid reparents the active tile and triggers
    async resize/fit on neighbour tiles, both of which momentarily blurred the
    active terminal to <body>; a character typed in that sub-frame gap was
    silently lost (e.g. hello → ello). A synchronous focus guard now
    reclaims the terminal the instant it blurs during the post-switch settle
    window, and the focus-retry loop no longer re-focuses an already-focused
    textarea (which cleared pending input). (#186)

  • GUI: Shift+Enter in an agent session now inserts a newline instead
    of submitting, so you can compose multi-line prompts for Claude/Codex.
    xterm sends a bare \r for Shift+Enter and drops the Shift, so the
    agents couldn't distinguish it from plain Enter and submitted. The
    custom key handler now intercepts Shift+Enter and writes Ctrl+J
    (\x0a) — the newline byte both agents accept with no terminal
    configuration. Plain Enter still submits; ⌘/Ctrl+Enter still toggles
    grid-project view. Works on all platforms. (#217)

  • GUI: scrolling discipline on mode switch and resize. Switching
    display modes (focused / grid / grid-project) now always snaps
    every visible tile to the bottom — mode toggles are deliberate
    user actions, so the previous "land wherever the buffer happened
    to be" behavior is gone. Resize keeps the existing 2-line
    sticky-bottom tolerance (#163). The viewport is never automatically
    moved away from where the user has placed it: when a window resize
    triggers a scrollback replay while the user is reading deliberate
    scrollback, the replay no longer yanks them to the bottom on
    completion. (#213)

  • GUI: three grid-mode regressions and lock-in tests. (1) The first
    time entering grid mode after restart no longer mis-anchors the
    scrollback replay baseline against xterm's 80-column default,
    preventing repeated spurious replays as DPR/fit jitter crosses the
    threshold. (2) Minimizing or restoring a session no longer triggers
    spurious scrollback replays in the remaining tiles when their
    column widths reflow. (3) After resizing the window, toggling the
    sidebar with ⌘S no longer strands keystrokes on document.body
    — the keyboard handler now routes through toggleSidebar() and
    the toggle re-asserts keyboard focus the same way setView does
    after grid/single transitions. Adds unit coverage for the rebaseline
    helper (applyRebaseline) and Playwright e2e regressions for all
    three (plus an R-control case asserting that legitimate window-resize
    replays still fire). (#208)

  • GUI: scrollback no longer renders at the narrow grid width after a
    single → grid → single transition, and live output no longer
    overwrites already-scrolled lines. The daemon now keeps a per-session
    raw-byte ring buffer (8 MiB) and re-streams it through a new
    REQUEST_REPLAY wire frame whenever the GUI's column count changes
    by ≥4 cols; the frontend term.reset()s on a scrollback_replay_begin
    event so the replay paints onto a clean buffer. Initial attach uses
    the same path, so reattach scrollback is no longer capped at the old
    500-row vt10x history. (#200)

  • Worktrees: surface a log warning when git fetch origin fails or
    origin/HEAD is not configured, instead of silently falling back to
    local HEAD (or worse, silently basing a new worktree on a stale
    cached origin/HEAD). Closes the spec gap in #192's "remote
    unreachable" success criterion.

  • Worktrees now branch from origin/<default-branch> (typically
    origin/main) instead of local HEAD. Previously, creating a
    session-backed worktree from a stale checkout produced a worktree
    on outdated code, wasting agent work on rebases and rediscovering
    already-fixed bugs. internal/worktree.CreateWorktree performs a
    best-effort git fetch origin and resolves
    refs/remotes/origin/HEAD before git worktree add. Repos with no
    remote, offline environments, or unreachable upstreams fall back to
    HEAD-based branching transparently. (#192)

  • GUI: switching from single-focus mode to grid mode no longer leaves
    the active session visually highlighted but unable to receive
    keystrokes (regression of #181). Single → grid is the only transition
    that fires a synchronous display:none flip on the active tile
    during renderGrid's parent class swap, which blurs the xterm
    helper-textarea; ResizeObserver-driven xterm fits on newly-visible
    neighbour tiles then re-blurred it ~10ms after the rAF that #181
    used to re-focus. setFocusedTile now polls across up to 8 frames
    and re-establishes focus whenever document.activeElement drifts
    off the target's helper-textarea, idempotently. The gate decision
    is extracted into a pure decideFocusAction helper with unit
    tests, and the regression class is now covered by Playwright E2E
    via a real xterm + Wails-mock harness. (#186)

  • macOS: "Restart Hive" killed hived but the new GUI never appeared.
    spawnNewGUI re-execed the inner Mach-O directly, which spawns a
    process but doesn't go through LaunchServices, so the relaunched
    Wails/WebKit window never gained focus (often never rendered). When
    running inside a .app bundle we now relaunch via open -n <bundle.app>; -n forces a fresh instance even while the dying
    parent is still around. Dev builds (binary outside a bundle) keep
    the existing re-exec path.

  • GUI: switching from single-focus to grid mode no longer leaves the
    previously focused session visually highlighted but unable to receive
    keystrokes. Visual focus (.term-focused) and keyboard focus
    (xterm helper-textarea) are now reconciled atomically through a
    single state-driven writer, so they can no longer drift across view
    transitions, modal open/close, or rename input. (#181)

  • Windows: starting a session with an agent (Claude, Gemini, Copilot,
    …) opened a bare cmd.exe prompt instead of running the agent. The
    session spawner unconditionally invoked <shell> -l -i -c <line>,
    but on Windows the shell falls back to cmd.exe, which treats
    -l -i -c as garbage and drops the command. The spawn path now
    branches on GOOS: Unix keeps <shell> -l -i -c <line> (load-bearing
    for fnm/nvm/asdf PATH setup); Windows uses %ComSpec% /C <line>
    with cmd.exe-aware quoting, which also correctly handles .cmd
    shims like claude.cmd. (#183)

  • GUI: terminal text no longer gets stuck displaying garbled glyphs in
    long-lived sessions until the user resizes the window. The xterm.js
    WebGL renderer can lose its GL context (browsers cap simultaneous
    contexts process-wide, so many-tile sessions can trigger this) or
    silently invalidate its glyph atlas on device-pixel-ratio changes
    and visibility transitions. Each session tile now recovers from
    context loss by re-attaching the WebGL addon (or falling back to the
    DOM renderer) and forcing a full repaint, and clears the glyph atlas
    on DPR changes and on becoming visible again. The GL context is also
    disposed explicitly when a tile is destroyed to reduce pressure on
    the per-process context cap. (#190)