Skip to main content

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 with helixgen 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 .hsp is enough to refuse every prune from then on, and the TUI offers no way to fix it from inside the app; tracked in docs/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 out in 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 (r on Device) before retrying rather than firing the same write again. Tracked in docs/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)

Source distribution for helixgen-tui 0.3.1
File Size Uploaded Provenance
helixgen_tui-0.3.1.tar.gz 72.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for helixgen-tui 0.3.1
File Interpreter ABI Platform
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page