Skip to content

feat(herd): port the sidebar to hqtui, as a folding tree - #547

Merged
ralyodio merged 1 commit into
mainfrom
herd-hqtui-onto-main
Sep 25, 2026
Merged

ralyodio merged 1 commit into
mainfrom
herd-hqtui-onto-main

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Recovers the hqtui port from PR #546, whose commits never reached main.

Why this PR exists

#546 was opened with herd-ui-default as its base rather than main, because it was stacked on #545. #545 merged to main first, so when #546 merged it merged into an already-merged branch. GitHub marks it MERGED and it looks done, but main never received src/herd-sidebar.mjs or the @profullstack/hqtui dependency. This is the same commit (b084216), cherry-picked onto main.

What it does

moshcode used hqtui nowhere. The TUI was entirely hand-rolled escape sequences, which is why it looked dated. This adds @profullstack/hqtui@^0.7.0 and ports the herd sidebar to it.

src/herd-workspace.mjs is now the tmux half (221 lines) and src/herd-sidebar.mjs is the view. The sidebar is a folding tree: one click opens a member, one click folds a herd in place, hover lights the row under the pointer, and actions are real buttons.

The local ~/hqtui checkout is 0.6.2 and has no tree widget. 0.7.0 does, with onRow/hovered/onSelectRow/onHoverRow, which is what made the folding tree straightforward.

Neither known hqtui trap tripped: collapseBorders is off with the reasoning written down, and no panel carries both a title and a subtitle.

Scope

Sidebar only. The PR body of #546 has the honest write-up of what porting herd-ui.mjs, herd-bar.mjs, herd-tile.mjs, rss-ui.mjs and tui.mjs would take. tui.mjs is a readline rewrite rather than a rendering port and wants its own PRD.

Dependency note

This adds the second runtime dependency. install.sh already handles that path (install_deps runs npm install --omit=dev in staging and fails loudly rather than shipping a CLI that cannot start), which is the fix from #505 after @profullstack/synconfig broke installs in 0.96.0. Verified locally: pnpm install then moshcode --version starts clean.

Testing

node --test "test/*.test.mjs" on the cherry-picked tree: 2726 tests, 2722 pass, 0 fail, 4 skipped. The lower count versus #546's 3514 is only because apps/pwa deps are not installed locally; CI installs them.

🤖 Generated with Claude Code

"are we using hqtui.com library for this tui? it looks pretty old and
outdated. upgrade it to hqtui"

moshcode did not use hqtui at all: zero references in src/ or bin/, and
the TUI is hand-rolled escape sequences throughout. So this is a port,
not a version bump. @profullstack/hqtui joins as a dependency at ^0.7.0,
a house package with zero runtime dependencies.

SCOPE. The herd sidebar, which is the surface Anthony is looking at when
he complains, and nothing else. src/herd-workspace.mjs keeps the tmux
half and only the tmux half; src/herd-sidebar.mjs is the new view. The
right-hand pane is still a real tmux pane running a real agent, because
no renderer can substitute for one.

WHAT THE LIBRARY BUYS. Three bugs came out of the hand-rolled shape and
none of them were ever about herds:

  - the click map was a second source of truth. sidebarRows() numbered
    the lines and renderSidebar() printed them, and when they drifted
    every click landed on the row below the pointer. hqtui's tree
    reports the row it drew each node on, so the map is built by the
    thing that draws it.
  - there was no hover, so a click had to be spent selecting, which is
    where the double-click came from. onHoverRow plus hovered is now
    the whole of it.
  - the restore path was escape sequences only. hqtui's Terminal
    restores on SIGINT, SIGTERM and an uncaught error by default.

A FOLDING TREE, IN PLACE. The herd is herds containing members, which is
a tree, and the expectation for a pane of things-containing-things is
that it unfolds in place on ONE click with the row under the pointer
lit. Clicking a herd folds it; clicking a member opens it, meaning shown
and given the keyboard. Nothing is swapped out for a different screen.
Actions are real buttons with onPress.

Neither known trap is tripped: collapseBorders only merges where two
bordered siblings touch and there is nothing here to merge, so it is off
with the reasoning written down; and no panel carries both a title and a
subtitle, which is where near-identical panels truncate differently.

The tests are the part worth having. renderToScreen draws the real view
headlessly and then presses real cells, so "is the thing that says api
the thing that opens api" is answered by clicking the cell that says
api, rather than by two pieces of code agreeing about a line number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown
Contributor

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​profullstack/​hqtui@​0.7.0801009996100

View full report

@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

0 finding(s) in the 7 file(s) this pull request changes.

Nothing in the files this pull request changes.

104 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 8 | **MEDIUM**: 85 | **LOW**: 11

Not introduced by this pull request. The full set is in the Security tab.

Severity Rule Location
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
HIGH sh-remote-script-execution install.sh:86
HIGH sh-remote-script-execution install.sh:90
HIGH sh-remote-script-execution install.sh:258
HIGH sh-remote-script-execution install.sh:269
HIGH sh-remote-script-execution install.sh:275
HIGH tls-verification-disabled src/dns.mjs:766
MEDIUM sql-template-interpolation apps/pwa/src/lib/moshpit-certs.mjs:44
MEDIUM sql-template-interpolation apps/pwa/src/lib/moshpit-certs.mjs:82
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:139
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:153
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:179
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:373
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:377
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:422
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:671
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:867
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:869
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:928

…and 84 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 37c776f into main Sep 25, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant