helixgen-tui
A terminal UI for helixgen: manage your tone library, setlists, and a Line 6 Helix Stadium over the LAN — from the terminal, no editor app.
⚠️ Unofficial tool — use at your own risk. Not affiliated with or endorsed by Line 6 / Yamaha. Line 6, Helix, and HX are trademarks of Yamaha Guitar Group, Inc. The MIT LICENSE disclaims all warranty.
Repo family
| Repo | What it is |
|---|---|
| helixgen-core | The helixgen Python package: libs, CLI, MCP server |
| helixgen | The Claude Code plugin — /tone, /setup, /device skills + marketplace |
| helixgen-tui (this repo) | Terminal UI for tones, setlists, and device control |
Install
uv tool install helixgen-tui
# or
pipx install helixgen-tui
Then run it as helixgen-tui or the shorter hxg.
Usage
The app is the librarian: browse and manage tones, setlists, and device sync, plus setting the active tone on the hardware — all inside a tabbed shell:
| Tab | Key | What it does |
|---|---|---|
| Library | 1 |
Browse tones, view details, filter (/), make a tone active, sync it to the device |
| Setlists | 2 |
Manage setlist membership and order, filter (/), sync a setlist or all setlists |
| IRs | 3 |
Push local IRs to the device, filter (/), rename/delete/prune device IRs |
| Device | 4 |
Device info, active tone, backup/restore, lock status, retry connect |
Press ? anywhere for the full key-binding reference, q to quit.
Fuzzy filter: / opens a filter on the Library, Setlists, and IRs screens
(on IRs it applies to whichever pane has focus — moving focus re-targets it, so
the newly focused pane gets ranked and the other returns to its native order).
Type part of a name — matching is an ordered subsequence, so jcm finds
"JCM800 Crunch"; matches sort best-first and the matched characters are
highlighted, and the cursor rides the top hit as you type. enter in a filter
parks on the highlighted row — the best match — and hands focus back to the
list, so s/p/d act on it instead of typing into the filter; it never
mutates anything itself (activate, sync, push, delete stay on their own keys).
The query stays live after enter, so you keep arrowing the narrowed list.
escape clears the
filter, and on IRs it unwinds one step at a time, innermost first — an open
rename prompt goes before a live query, so a filter typed before renaming takes
a second escape. The add-tone picker in Setlists filters the same way, where
enter adds the highlighted match.
Tone editor: press enter on a Library tone to open its signal chain. The
chain renders left-to-right — an input node, the blocks (both DSP paths stacked
when parallel-routed), then the output node. Arrow keys walk the chain; tab
switches to the params inspector, where left/right nudge a value and enter
types one. Structural keys: a add a block, x remove, b bypass/enable, w
swap the model. Selecting the output node edits its level/pan; the input node's
source is read-only. Add/remove refuse on a parallel-routed path. Edits write to
the local library file on s — never to the device.
Setlist sync: ✓ marks a setlist the device mirrors; ○ is a local
draft. S syncs the selected setlist and opts it into mirroring; A syncs
every ✓ setlist and never opts a draft in — so a library where nothing has
been synced yet shows (no synced setlists to sync) in the confirm. Both also
remove device presets for tones you have unsynced since the last run,
which is why the sync-all confirm says so; a preset a live setlist still
references is left alone and named in the status bar. S on the Library
tab runs the same engine call for one tone's setlists, so it carries the same
mirror deletes — with no confirm at all; tracked in docs/BACKLOG.md #32.
Offline-first: the app works fully with no device on the LAN — Library,
Setlists, and IRs stay browsable from local state. Device-mutating actions
(activate, sync, push, backup, restore, ...) simply refuse with a reason in
the status bar when no Helix is reachable, and reconnect automatically (or
via r on the Device tab) once one is. A device that is reachable but
refuses or aborts an operation is a third state: the header stays connected
and the panel or status bar shows the engine's own reason and remediation
text, rather than the app going offline over one dropped frame. The exception
is a background probe that itself fails: the header does go offline, carrying
probe failed: … as its reason, because a probe that can't complete says
nothing about whether the device is usable.
Design principle: slots are invisible. The UI speaks in tones and
setlists only — slot addresses like 5A are an implementation detail the
user never sees or types.
Note: device restore and device-side tone delete are not yet wired — they await core-side verbs (a restore that carries its target preset, and a single-pool-preset delete). The UI surfaces a clear reason until then. See
docs/BACKLOG.md#6.Three device-IR verbs can report something short of a clean success, all by design: deleting an IR (
d) can half-succeed — the Helix removes the registry entry but leaves the backing file behind, reported as "registry entry removed; backing file left on the device"; clean that up withhelixgen device delete-ir <hash> --force-wedge --yes(the TUI does not offer the forced delete). And prune (P) refuses in the status bar, without opening a confirm, whenever the engine can't verify some local tones' IR references — a confirm it could only fail is worse than no confirm. One library tone with a missing.hspis enough to refuse every prune from then on, and the TUI offers no way to fix it from inside the app; tracked indocs/BACKLOG.md#28. And pushing an IR (p) surfaces the engine's own refusal verbatim — e.g. "IR 'foo': hash_mismatch" — where earlier versions reported a bare success over a push that never landed.Also known: long device operations — sync all and prune especially — can report
timed outin the status bar after ~5s while the operation is still running on the device and goes on to complete. The failure is in the reporting, not the write; nothing is rolled back. Re-check the tab (ron Device) before retrying rather than firing the same write again. Tracked indocs/BACKLOG.md#26.
The long-term goal is full parity with the Helix Stadium desktop app
(tracked in helixgen-core's docs/stadium-app-parity.md); this v1 ships the
librarian and the tone editor (param editing plus signal-flow editing —
block add/remove/swap, bypass, output level/pan), with more screens (global
settings, tuner/meters) to follow. See docs/BACKLOG.md.
Development
Managed with uv; the package layout is
src/helixgen_tui/, depending on helixgen[device]>=0.31 from PyPI (never
vendor core source here). The 0.31 floor is a runtime one, not just a test
one: below it a pushed IR never re-appears in the device IR listing (core #38),
so the IRs tab misreports what is on the device.
uv run pytest # test suite
uv run ruff check . # lint
uv run helixgen-tui # run the app
uv build # sdist + wheel
The suite is offline by default: tests/live/ (the real device port against
real hardware, real device writes) hard-skips at collection unless
HELIXGEN_TUI_LIVE=1 — every skip in a normal run is that gate. To run it
against a Helix Stadium on the LAN:
HELIXGEN_TUI_LIVE=1 uv run pytest tests/live -q
It needs port 2002 TCP-reachable, an ingested block library, and a resolvable
device IP (HELIXGEN_HELIX_IP, else a helixgen device discover record) —
without any of those it skips rather than fails. An installed helixgen below
0.31 is the one hard failure: below it a pushed IR never re-appears in
list-irs (core #38), so the IR tests would read as a TUI regression. All local
helixgen state is
redirected to a scratch dir for the run and every artifact is HGTEST-prefixed;
the safety model and the deliberately excluded verbs are documented in
tests/live/conftest.py.
Two recoveries worth knowing before you need them. A run killed outright
(SIGKILL, not Ctrl-C) leaves the machine's real advisory all device lease
held for up to its 1800s TTL, which blocks every other helixgen process on the
machine — clear it with helixgen device unlock --force. And a
~/.helixgen changed during the test session failure is not necessarily a
suite leak: another helixgen process (an editor, a parallel agent, a shell)
touching the real home fails the session the same way. Tell them apart by
re-running under an isolated home — HELIXGEN_HOME=$(mktemp -d) uv run pytest
— which still fails only if the suite really did leak.
CI (GitHub Actions) runs ruff + pytest on every PR and push to main;
publish.yml releases to PyPI via OIDC trusted publishing on v* tags.
Metadata
Release files for helixgen-tui 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| helixgen_tui-0.3.1.tar.gz | 72.8 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| helixgen_tui-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 148.6 kB
Release files / helixgen_tui-0.3.1.tar.gz
| Download URL | helixgen_tui-0.3.1.tar.gz |
|---|---|
| Size | 72.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
31b98fc618d03af26fea1695ce66d89e20da226f0b3293e01ae7fbc114868b74
|
|
BLAKE2b-256 checksum How to use checksums |
4f34e5b22241970466110f5540e051cb3c274da459a397a0e3ea7e103b65cbc1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.
Transparency logRelease files / helixgen_tui-0.3.1-py3-none-any.whl
| Download URL | helixgen_tui-0.3.1-py3-none-any.whl |
|---|---|
| Size | 75.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
074bb3593038cf82dd2dbced6f93b999d5fd11fbbf9a362c1b9b7bea07ae6730
|
|
BLAKE2b-256 checksum How to use checksums |
0fac18b48719c88a041f8dfa606c3fccd883bbab8731e842fd20544ae8601cac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.
Transparency logSecurity for helixgen-tui 0.3.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for helixgen_tui-0.3.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 28, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.