Skip to content

About

POSIX-friendly TUI file manager for the terminal, built around a real embedded bash shell — mouse-driven panels, context menus, and dialogs. Go, tcell/tview.

Topics

Resources

Contributing

Stars

3 stars

Watchers

2 watching

Forks

Repository files navigation

License: Apache 2.0 CI (Linux) CI (Linux ARM64) CI (macOS)

breakthrough

A mouse-and-menu-driven TUI file manager for your POSIX-compliant terminal, built around a real embedded bash shell — written in Go.

Hero Picture

breakthrough puts classic GUI file-manager idioms — a browsable panel, context menus, floating dialogs, clickable breadcrumbs — over a real bash session, without ever leaving the shell. It's built with tcell and tview; it is not integrated into Bash itself — no fork, no patch, no hook into Bash's own internals. Its console is a first-class citizen all the same: its own history, path completion against the current directory, multi-line scripting, and every command running through your actual $SHELL, started the same way an interactive login shell would be — so it sources that shell's own startup files and expands its aliases, whichever shell that actually is — not a reimplementation of one.

 

 

It is explicitly not an attempt to rebuild Midnight Commander — the goal is its own UX philosophy, closer to classic GUI file managers, just in the terminal.

Features

  • Panel-based directory browsing: arrow-key and mouse navigation, a clickable path breadcrumb bar with Start/Root/Home/Back/Forward — its history covers a trip into search results or the trash exactly the same as a real directory, and Back/Forward into any of them restores the cursor row it was left on, a search's own results included, shown again exactly as they were rather than a live re-run — plus Reload (⭯, right before the path itself), re-reading the current directory straight from disk for anything this app has no other way to notice on its own: another process changing files underneath it, a network/mounted filesystem's own content changing, and so on — sortable Name/Size/Modified columns, and file-type indicators (directory, symlink — including broken and multi-hop chains, socket, FIFO, device, mount point, hard link) matching Midnight Commander's own glyph scheme. Every entry Enter can navigate into — a directory, a symlink to one, a mount point, or .. itself — gets its own name highlighted dark yellow (just the name, not the trailing / or a symlink's -> target arrow), so folders stand out from plain files at a glance. Beyond that, a name's own text color also tells them apart: dark green for executable, red for a broken symlink, a darker red for anything the current user can't actually read (checked with a real permission check, not just Mode's bits — a /proc entry included), cyan for a working symlink to a file, orange for a socket/FIFO/device, magenta for a recognized archive extension, and a dim gray for a dotfile/ dotdir (unless one of the other cases above already applies).

  • Columns that never lie: Size and Modified take exactly the width their content needs — six columns for human-readable sizes, ten for a Unix timestamp, nineteen for a full date — and the name column absorbs the difference. Switching either format reflows them immediately, and the column header is simply "mtime", short enough never to constrain the column. When something has to give it is the filename, shortened in the middle (annual-re…final.pdf) so both the identifying start and the extension survive. This matters most in split view, where each pane is half as wide: before, Size collapsed to … and Modified disappeared off the right edge entirely.

  • A live filter, tucked behind a compact "Ÿ" button in the top row (an "Nx" count appears right before it once one or more are actually narrowing the listing — glowing the same slow green breathing pulse the header's own "@" connection button uses (see below), or turning solid red instead if one is currently hiding everything a directory would otherwise show): click it, or press /, to open a small dropdown with three independently combinable rows, each narrowing the listing live as you type. A compact "f" button sits right next to it (one column of breathing room between the two) — click it, or press f, to open Find instead: a separate, draggable dialog with its own close button, for locating a file by name or content anywhere under a chosen starting directory, by find or locate.

    • Glob/regex — the original filter, with a Glob/Regex toggle for how the pattern is interpreted and its own checkbox to switch it off without losing what's typed.
    • Size — comparison expressions like > 1m, >= 500k, or a range by joining two with and (> 1m and < 1g). Units are b/k/m/g/t, binary (1024-based), the same convention the Size column's own human-readable mode already uses; a bare number means plain bytes.
    • Modified time — before/after/between ... and ..., each side either an absolute date (2026-09-01, optionally with a time) or a relative one (7 days, 2 hours ago); a bare relative expression on its own (last 7 days) means "modified within that span".
    • Labels — ten small toggles, one per color label (0 "no label" through 9), each shown as its own colored swatch with no number or text alongside it — id 0 shown in the dropdown's own plain background rather than a color, since "no label" has none of its own. Toggling more than one ORs them together (label 2 or 3, say, shows both at once). Left/Right move between the ten swatches while this row has focus; the one the keyboard is currently on is marked < > around it, its own color always staying exactly what it already was — never swapped out just because the keyboard happens to be on it.

    A sixth row, Exclude dirs, sits below all four: not a filter of its own, but a single on/off switch that applies to all four at once — while on, a directory is never hidden by any of them, however it would otherwise have matched (or failed to match); only plain files are ever actually filtered.

    Typing into any field auto-activates its own row, the same way it already did for glob. Tab/Shift+Tab cycle through all nine of the dropdown's own pieces (checkbox + field for size and modified-time, checkbox + mode button + field for glob, the Labels row, plus the "Exclude dirs" checkbox), Space/Enter toggles whichever checkbox (or, on the Labels row, whichever swatch) has focus, / — once the dropdown is already open — jumps straight to the next of the three expression fields instead (the same "press it again to advance further" trick Ctrl+T already does for the tab switcher; safe to repurpose since a bare filename can never contain / in the first place; the Labels row has no field of its own, so / from it jumps straight past to whichever field comes next), and Escape closes it from any of them. Carries over across a directory change by default (filter_persistent), so browsing a whole tree with the same filter switched on is the normal way to use it, not a special case; set it to false to go back to every new directory starting unfiltered instead.

  • The seven nav buttons (Start/Root/Home/Up/Back/Forward/Reload) at the very start of the path bar are real, highlighted buttons now — a plain background-colored square either side of each glyph, with its own separator column between one button and the next. Root (/) sits right next to Start and jumps straight to the filesystem root — the same destination the path breadcrumb's own leading "/" already links to, just as a second, consistently styled, easier-to-spot target for it.

  • Tabs: several directories open at once in one window, each keeping its own history, filter, sort order, selection and cursor position, so switching away and back leaves everything exactly as it was. ^1 through ^0 (or Alt+1...Alt+0 — bound to the same tabs, for terminals that can't report one or the other) jump straight to a tab, ^Tab/^Shift+Tab step through a switcher showing every tab's full path. t/^T opens the same switcher without moving, and works everywhere the others might not. A compact numbered strip sits beside the filter — numbers rather than paths, so the header row's width stays put as you navigate — and shrinks to just a "+" while only one tab is open. The layout is saved on exit, but a bare breakthrough opens in the current directory rather than reopening it — set restore_tabs = true to resume your last layout instead. A directory named on the command line always wins regardless of this setting.

  • Split view (s): two of those tabs on screen at once, side by side (with a plain background-colored gap column between them) or stacked (z then o flips it, or set split_stacked once and forget it). With a single tab open, s opens a second one on the same directory — the usual starting point for copying between two places in one tree; otherwise it pairs you with the tab you last split with. To choose the other pane yourself, the tab list (t) gives every row a ◫ button, including its "+ New tab" row, which creates a tab and splits with it in one step. Tab, or a click, moves between the panes, and the one you are in keeps the keyboard, the context menu and every shortcut; the highlighted row shows which that is. Switching tabs while split replaces only the pane you're in — the panes never swap sides. The split is saved and reopened alongside the tabs. See docs/user-guide.md for the details.

  • A primary keyboard layer of plain letters — c/x/v copy/cut/paste, d/D Trash/remove permanently, r rename, i/I Properties/ Details, s/S split/swap panes, and more — active only while the file listing itself has keyboard focus, never while typing in the filter box, the path editor, or the command line. No modifier key means no terminal/layout/multiplexer to trip over — the same approach ranger, nnn, lf and vifm all settled on. Related, rarer actions sit behind a chord (a letter, then one more within about four seconds by default — "Chord timeout (ms)" under Options, Behavior's own Miscellaneous subsection — with a countdown in the status bar and a clickable legend in the button bar): g to jump somewhere (gg top, gh home, gr /, gb Trashbin), p for permissions (pm chmod, po chown), z for display toggles (zs size format, zt time format, zi Nerd Font icons, zl label — set/clear one of 9 colors on the focused or checked row(s), zo split orientation, zw swap panes), o for Options (oo the screen itself, om mouse reporting on/off — quick, direct toggles without opening the screen at all). See docs/user-guide.md.

  • No function keys anywhere in the application, and — as of the latest round — no Ctrl-letter bindings either, apart from the two things a plain letter genuinely cannot do (quit/cancel from literally anywhere, even mid-edit) and one narrow extra reach for the tab switcher (^T alongside t, for walking to the next tab while the switcher itself is already open). Mouse reporting's own former ^_ — a single, narrow toggle that fired even with a dialog open or a text field focused, since a Ctrl combination safely can where a plain letter never can — was deliberately retired anyway in favor of om above, a considered trade-off rather than an oversight.

  • A context menu on mm (the m chord's own doubled prefix, the same "gg"/"oo" shape every other family uses for its main destination) or right-click, showing only what actually applies right now rather than a fixed list of everything it can ever do: Look, Edit, Execute, Open with… (all three of the latter dropped for a directory), Rename, Copy/Cut/Multiply, Paste (only once the clipboard has something in it), Move to Trash, Properties (editable — name, permissions, click a bit or type the octal value directly, owner and group via a scrollable picker of every local user/group, modified date and time), plus four ▸ submenus that replace the list in place when chosen (Windows Explorer's own cascading-menu idea, without needing room to open beside it): "Label" (set/clear one of 9 colors — see zl above — each shown as a small colored swatch next to its name), "More actions" (New file, New dir, tail -f, chown, chmod, Sed Replace, Batch rename, Undo last rename, Compare, Rsync, Compress…, Extract/Extract-delete-original (the latter two only for a recognized archive), Remove, Paste following symlinks), "Selection" (Select all/Deselect all/glob-pattern Select +/-), and "Tabs & Split" (New/close tab, Switch tab..., Split on/off, orientation and Swap panes — the last two only once a split actually exists). Right arrow drills into a submenu with no Enter needed; ◂ Back, Escape, or Left arrow step back out one level at a time — ◂ Back is also a clickable button in the status line itself whenever a submenu is open, not just a row inside the list. Browsing the Trash itself replaces the whole menu with just Restore/Empty Trash/Properties, since almost nothing else still applies there. Once open, l/e/E/r/c/x/d/i — the same letters Look/Edit/Execute/ Rename/Copy/Cut/Move to Trash/Properties already have on their own — fire that entry directly, without arrowing down to it first; one whose own entry isn't currently showing does nothing. o/t/A do too, for Open with…, tail -f, and Deselect all — all three also directly reachable as mw/mt/mA from plain browsing (not mo: the plain-letter layer's own key for Open with… is W, since lowercase o is already the o chord's own prefix), without opening the menu at all. m again (mmm from plain browsing) fires Multiply, the one entry with no keyboard route of its own anywhere else, since its own natural letter is already this chord's own prefix key. mf/md reach New file/New dir directly from plain browsing too, without opening the menu at all — both create their new entry directly inside the active panel's own current directory (remote-aware the same way Rename already is), prompting only for a name. Every entry one level into a submenu has its own mnemonic too, each reusing whichever letter/case that same action already answers to elsewhere (chown/chmod reuse the p chord's own po/pm, Compress/Extract the j chord's own jc/je/jE, and so on) — the status line's own legend always lists the currently open submenu's own entries, the same way it lists the top level's.

  • Copy/Cut/Paste (c/x/v, or the context menu): works on the whole current selection, not just one file. Pasting into the very directory a file is already in, or a directory into one of its own subdirectories, is refused outright rather than started at all — the first would have destroyed the only copy there ever was, the second would recurse into itself without any bound. Paste runs in the background — a file that already exists at the destination opens a small dialog (Overwrite, Skip, "Merge into existing folder", an "all" variant of each for the rest of this Paste, or apply "only if the source is newer"/"only if the source isn't empty" to every conflict it still runs into) without blocking anything else in the same Paste: whatever doesn't conflict keeps copying/moving while that dialog is up, and a second conflict found before the first is answered queues behind it — shown as "(N more waiting)" right in the dialog's own message — rather than stacking a second dialog on top. Unlike every other dialog here, clicking outside it does nothing — one of its own options, or Escape, is the only way past a conflict, so a stray click can never leave one stranded, half-answered forever. Overwriting a directory replaces it entirely (nothing left over from whatever was there before — the right choice when "overwrite" needs to mean "make this identical to the source", not "patch it"); Merge is the explicit alternative, keeping whatever the source doesn't also have. "Auto-merge directories" under Options (off by default, set separately for Copy and Move) skips this dialog entirely for a directory-vs-directory conflict and merges right away every time — a file-vs-file or file-vs-directory conflict always still asks, since merging has no meaning there. Starting a further Paste while one is already running queues it rather than running it alongside the first or replacing it outright — shown as "(+N queued)" right in the status bar's own progress line — and it starts automatically the moment the one ahead of it finishes, in the order each was asked for. Ctrl+C pauses a running Paste and asks what to do next — continue, cancel just this job and let whatever's queued run, or cancel everything; whatever item was already mid-transfer finishes normally for a Copy, or, for a Cut, stops immediately with its own source left untouched, but only if you actually choose to cancel. A backgrounded Rsync or Compress/Extract is cancelled separately: Ctrl+C with no Paste running asks to cancel both together (dropping both their own queues), while the "mR"/"mC" chords and a clickable "✕" on each progress segment reach each one individually, "current job only" or "current plus its own queue" apart from the other. Any real failure (permission, a full disk, ...) is collected rather than stopping at the first one, and reported once the whole Paste is done. Every open tab showing the destination reloads automatically as items actually land, not just once the whole Paste is fully done — including while an unrelated conflict's own dialog is still sitting open, since whatever doesn't conflict keeps landing regardless; Cut also reloads every open tab showing one of the moved items' own source directories, live, as items actually leave, so a tab something was cut from never keeps listing a file that's actually gone — Copy leaves its own source list alone, since nothing there was ever removed. Whatever's currently on the clipboard shows two ways: every row it holds gets a full-row tint — a slightly bluish-tinted grey for Copy, a slightly pinkish-tinted grey for Cut, a deliberately matched pair rather than two shades of one plain grey, so a Cut selection reads as visually different from a Copy one at a glance rather than needing a brightness comparison. This tint stays visible even on whichever row the cursor happens to land on: the ordinary focus highlight wins outright over it while this panel actually has keyboard focus, so the cursor's own position among several tinted rows is never ambiguous, but a dimmer variant of the same tint takes over once focus moves elsewhere, still clearly Cut- or Copy-colored rather than fading to a plain, indistinct gray — across every open tab showing that row, not just the one Copy/Cut was pressed in; and the status bar names it — "Copy: 3 files, 1 dir" or "Cut: ..." — right after the button-bar chord countdown's own spot, for as long as there's something to Paste. Once a Paste actually starts, that same spot switches to its own live progress instead — a spinner, "Copying"/"Moving" and how many of the selection's own top-level items are done, and a two-row progress bar packed into one line of half-block characters (the top half is that same item-count fraction, the bottom half is the file currently being written's own byte progress), plus whichever real file is being written right now (its bare name, e.g. inside a large directory this Paste is still working through). A one-time background scan of the whole selection's byte size (started alongside the Paste itself, never blocking it) adds two more things once it's done: a single character before the bar showing what percentage of the total bytes has copied so far (the same shrinking/filling block style the chord countdown uses), and an estimated remaining duration after the bar. A same-filesystem move is atomic regardless of size, so a Cut within one filesystem usually finishes too fast for any of this to show anything at all — expected, not a bug: there's nothing to report progress on. Plain v follows whichever "Follow symlinks" default is set under Options for Copy or Move (off by default, set separately for each): off recreates a symlink as a symlink at the destination, on replaces it — and any symlink nested inside a pasted folder — with a real, independent copy of whatever it points to (recursively, through a multi-hop chain too), instead. V (Shift+Paste, or the context menu's "Paste, following symlinks") flips that default for this one paste only, without changing the setting itself. Works for both a Copy- and a Cut-marked clipboard alike; for a Cut, only the original link itself is removed afterward — never whatever it pointed to, however far away that actually lives (a different filesystem, a network mount). V only asks for confirmation first when it's actually the thing turning dereferencing on (the configured default was off) — dereferencing can turn a small, instant symlink into an arbitrarily large copy, so activating it is never a single, undialogued keypress; flipping it back off for one paste needs no such confirmation, since that's the safer direction. Two more Copy/Move settings round this out, both under Options and set separately for Copy and Move: "Preserve attributes" (on by default) carries the source's own permissions, ownership, and modification time over to the destination — turning it off leaves the destination at whatever creating it just produced. "Stable symlinks" (off by default, matching Midnight Commander's own default for the equivalent option) rewrites a copied symlink's target to point at its new location if that target lives inside the tree being copied, rather than keeping the original target verbatim — without it, such a link can end up pointing back at the original source (an absolute target) or nowhere at all (a relative target that climbed out of the copied root and back in by its old name) once that source is later moved, renamed, or removed.

  • Move to Trash / Remove: d or Entf moves the current selection to your own trash — recursively for a directory, no confirmation by default, since that's the reversible action by design ("Confirm before moving to Trash" under Options, off by default, asks first anyway for anyone who wants that extra safety net). D, Ctrl+Entf (best-effort — terminal-dependent; D is always the reliable one), or the context menu's "Remove" permanently deletes instead (a file like rm, a directory recursively like rm -rf, empty or not), always behind a confirmation dialog with Cancel preselected — a single stray keypress can never confirm it by itself. "Go to Trash" (the g chord's own gb) jumps straight into it without needing to know its path; "Restore from Trash" (r, while browsing it) and "Empty Trash" (D, same confirmation) round it out. Restoring something whose original path now has an unrelated file sitting on it — recreated after the original was trashed, say — opens the exact same conflict dialog a Paste collision already does (Overwrite/Skip and their "for all" and "if newer"/"if not empty" variants, Skip preselected as the safe default), rather than silently overwriting it or refusing outright with nothing but an error; a multi-item restore can pull items whose own original locations were entirely different folders, each resolved independently. Persistent by default — lives under ~/.local/share/breakthrough/trash, so it's still there tomorrow, even across a login session boundary — or session-scoped via trash_persistent = false in your config, under $XDG_RUNTIME_DIR instead, gone once the session ends; running as root (e.g. via sudo) always gets the persistent path regardless, since root has no real session of its own for a session-scoped trash to mean anything for. Kept from growing forever by two settings checked once at startup, not on every single trash operation: trash_max_age_days (30 by default — anything older is removed unconditionally) and trash_quota_percent (10 by default — a backstop, oldest item first, only if age alone didn't already bring the trash back under that share of the filesystem it lives on); either one is 0 to disable it. Anything actually removed this way is reported once, on the next start. Browsing the trash itself ("Go to Trash") shows each item's own original path in place of its real on-disk name (a collision- avoidance hash you'd otherwise have to squint past — two files trashed from the very same location, more than once, still stay distinguishable this way) and labels the Modified column "Deletion time" instead — both, like the Modified column always has, respecting the Options overlay's timestamp-vs-formatted toggle and the column's own sort.

  • Multiply (mm, or the context menu): creates one or more copies of the selection right beside it — a directory works just as well as a file, since it's an ordinary Copy underneath, never a Move. A guided dialog, not a pile of every option shown at once: a Strategy dropdown picks Numbered (default, counts up from 1 until a free name is found), Fixed suffix text (the same literal text every time — duplicating the result again doubles it, report.txt_copy_copy, rather than retrying automatically), or Date/time — computed once, with its own second dropdown ("Date/time format type") picking Go format string (default), Strftime-style Format, or Unix timestamp; the format field below it holds each syntax's own independently- edited example text (switching back and forth never loses either one), or — once Unix timestamp is picked — is disabled and shows today's real timestamp instead. Only the current strategy's own fields ever appear at once, never every strategy's combined. The suffix always lands after the original name's own entire text, extension included: archive.tar.gz duplicates to archive.tar.gz_1, never archive.tar_1.gz or archive_1.tar.gz — nothing about a basename is treated as more "real" than the rest of it just because it follows a dot. A live Preview line shows the exact name the first duplicate would get right now, given every field's current value; Cancel and Duplicate sit bottom-left/bottom-right, the same as every other dialog's own confirm/cancel pair in this app. Whatever's chosen becomes the new default shown next time (Options → Behavior → Duplicate) — the one setting group in this whole app that adapts itself this way — but only once "Duplicate" is actually pressed; Cancel never touches the sticky default.

  • Execute (E, or the context menu, files only): runs the current entry directly, full screen — the same path typing its path into the command line already takes, just without having to type it out. Does nothing for a directory; the real shell reports "Permission denied" for a file that isn't actually marked executable. Not supported yet for a remote connection.

  • Open with… (W, or the context menu, files only): runs any program you type against the selected file instead of always the configured editor — prefilled with whatever you typed last time, handed to your real shell exactly as typed (so a multi-word command with its own flags, like libreoffice --writer or code -w, works the same as at a real shell prompt). Works for a remote file exactly like Edit already does: downloads a local temp copy, runs the typed command against it, and uploads the result back over the connection only if it actually changed.

  • Sed Replace (S, or the context menu): runs a real sed(1) substitution against the current selection — one file or several, not a directory tree. A guided Find/Replace pair (Regex, Extended regex -E, Case-insensitive, and Replace-all-per-line toggles) builds the script for you, or drop straight into the advanced field and write the sed script yourself for anything real sed can do — address ranges, multiple commands, backreferences. Always previews first, as a Name/Line/Excerpt table — one row per changed line, skipped files listed with why — computed in the background with a live "Checking N of M" status; only Apply, behind the same Cancel-preselected confirmation, actually writes, optionally keeping a .bak of each original first. Never uses sed's own -i: GNU and BSD/macOS sed take incompatible arguments for it, so this always runs sed as a plain filter and writes the result back itself.

  • Batch rename (context menu): renames a whole selection through a fixed pipeline of steps — Search & Replace (literal or regex), Case (UPPER/lower/Title/Sentence), Trim (drop N characters off either end), Template (rebuild the name from {name}, {ext}, {counter}, {parent} and {date} tokens — {parent}_{date}_{counter} turns IMG_0042.JPG into Holiday_2026-03-09_001.JPG), Numbering (a zero-padded counter as prefix or suffix), and Extension (lower/upper/remove/replace) — with the steps listed down the left and the selected one's own settings on the right. A step left alone does nothing; there is no separate on/off switch to also remember. Search & Replace and Case only ever touch the name, never the extension, and Extension only ever touches the extension, so a case transform can't quietly rewrite .JPG behind your back. The whole selection is previewed live, old name beside new, updated on every keystroke rather than behind a "Preview" button — unchanged rows dimmed, a ● beside each step that currently changes something, a help line explaining the selected setting, and any collision (two files landing on the same new name, or a name already taken by something that isn't moving out of the way) shown in red with the reason, right where it would happen. A rename chain (1.txt→2.txt while 2.txt→3.txt, or an outright swap) is not a collision: the renames run in the order that makes it work, through a temporary name where needed. Case-only renames work on case-insensitive filesystems too, regex replacements accept $1 or sed-style \1, and a folder's name is never split at its dot unless you ask. In the preview, Space skips a row (it's neither renamed nor numbered), u/d arrange the numbering order by hand, n/p and c/C jump between changes and conflicts; Numbering can also count by name or by modification time, forwards or reversed. A whole pipeline can be saved as a named preset and loaded back later — plain JSON files under ~/.config/breakthrough/rename-presets/. Nothing is written until Rename is confirmed, and "Undo last rename" reverses the whole batch afterwards. See docs/user-guide.md for the step reference.

  • Compare (C, or the context menu): "are these two things the same, and if not, what's different" — for two files (mark them, or use split view with the cursor on one in each pane) or two whole directory trees. Two files get size/modified side by side plus an immediate verdict from the same size+time heuristic rsync's own default sync mode uses, an on-demand SHA-256 hash for a certain answer, and a real line-by-line diff — a side-by-side view, each pane independently scrollable (Up/Down/Page Up/Page Down, Tab to switch panes) or linked to scroll together via the view's own toggle button — for text. Two directories get a full screen listing every difference and every one-sided path — a one-sided directory is one row, never descended into, so an old backup folder doesn't turn into thousands of lines — with a key to switch between the quick check and a real hash, and to copy a one-sided item across after asking. See docs/user-guide.md for the details.

  • Rsync (R, or the context menu): a real rsync(1) invocation, built from a small dialog rather than typed by hand — Source/Destination (pre-filled from the current selection or panel, and the split view's own other pane where one's open), Archive/Compress/Delete/Dry run as a toggle list, and — the one choice this dialog makes impossible to get wrong by accident — an explicit "Copy the folder's contents in (not the folder itself)" switch, instead of leaving that distinction to whether a trailing "/" happened to be typed on the source path the way a bare rsync invocation always has. Source/ Destination already know about a tab's own SFTP connection: pre- filled as user@host:path instead of a bare local one when that tab is currently connected, its own port carried through to a real -e 'ssh -p PORT' flag rather than guessed — the same applies to an address typed straight in by hand for a host never connected to via the Connect dialog at all, recognized by rsync's own real disambiguation rule (a colon before the first "/" names a host). Two buttons, "Pick source tab…"/"Pick destination tab…", open a list of every currently open tab as an alternative to typing or trusting that one-shot default — picking one fills the field exactly as if Rsync had been opened fresh from that tab, connection included. Syncing between two remote hosts at once shows a warning line: rsync -e ssh has no server-to-server mode of its own, every byte still relays through this machine over two separate ssh connections, never directly between the two remote ends. A live preview line shows the exact command that would run, --delete called out in its own warning color the moment it's turned on, right up until either button runs it for real: "Run" hands it to a real rsync process with the real terminal attached, the same way the embedded bash line already runs anything that benefits from one, so --info=progress2's own live progress line renders correctly; "Run in background" instead keeps breakthrough itself fully usable — Copy/Cut/Paste included, at the same time — while its own live percentage shows in the status bar, queuing behind an already-running background rsync the same way a second Paste already queues behind one still in flight. "Run in background" refuses outright — pointing to "Run" instead — when Source or Destination is still a connection that last authenticated with a typed password: a background run has no attached terminal for ssh's own interactive password prompt to ask on, so silently reusing one there would otherwise hang or fail with a bare, confusing "Permission denied". See docs/user-guide.md for the full picture.

  • Network Tools (j then n) and Hardware Tools (j then h): two full-screen, browsable catalogs of built-in commands — Network Tools (Ping, Nmap, ip, route, ss, getent, wget, nslookup, dig, netcat, curl, a tail -f Logviewer) and Hardware Tools (lsblk, lsusb, lscpu, lsmem, lsdev, hwinfo, inxi, lsscsi) — real external commands, never reimplemented, the same approach Rsync above already takes. An entry that needs one — a host, a URL, a database key — asks for it in a small field first; every one of them then streams its live output into its own small, draggable tool window, non-modal so the Toolbox screen and the panel underneath both stay usable while it runs. See docs/user-guide.md for the full catalog.

  • Mounts (j then m): a third full-screen catalog, kept deliberately separate from the Toolbox above — a read-only, live table of every currently mounted filesystem (real storage only, pseudo filesystems like proc/sysfs/tmpfs left out), built from the real findmnt command. Cross-references each mount's own target against /etc/fstab to mark it "Persistent" (survives a reboot) or not (mounted by hand at some point since), and flags bind mounts, so it doubles as a quick answer to "what will still be here after I reboot this box?" See docs/user-guide.md for the full picture.

  • Compress (j then c, or the context menu's "Compress…") and Extract (j then e/E, or the context menu's "Extract"/"Extract, delete original"): real archive creation and unpacking, through a real external tool (zip, tar, gzip, bzip2, xz, zstd, or 7z) for Compress, plus RAR (via the real, freeware unrar) for Extract only — creating a .rar needs the proprietary rar binary instead, which this app doesn't depend on, so it never appears as a Compress format — never a reimplementation either way. Compress archives the current selection — a file, several files, or a whole directory — into a new file right beside it, or, once a split is active, straight into the other pane's own directory instead; pick a Format, type an Output name, a live Preview line shows exactly what will be created. Extract unpacks the archive under the cursor in one step, without first entering it — into its own directory, or, once a split is active, the other pane's own current directory, the same default Rsync/Compare already use. Either direction's own "other pane" may be a remote SFTP connection too: the real archive tool still only ever runs locally, but the result is compressed/extracted into a local staging copy first and then uploaded, transparently, with its own "Uploading" status while that happens. jE additionally moves the original archive to the Trash once extraction has actually succeeded — asking first, and naming the fallback plainly as a real permanent delete, if that ever fails outright. All of this runs in the background, the same as Copy/Cut/Paste — no terminal takes over the screen, and the affected directory reloads on its own once it's actually done.

  • Three rows below the panel, each with its own job. First, a real shell command line (with its own history — shared with $HISTFILE if you've set it, ~/.bash_history otherwise regardless of your actual shell, a deliberate choice so this line's own history always behaves like a bash one's — and cd handled directly rather than uselessly changing a subshell's own directory), which expands when clicked into — full width, no prompt, growing upward toward mid-screen with a "Bash Prompt Editor" legend above it — for multi-line bash scripting (Enter runs the buffer, Ctrl+J or Alt+Enter inserts a newline instead — Ctrl+J always works, Alt+Enter is intercepted by some terminal emulators for their own use; Up/Down recall history too, not just Ctrl+P/Ctrl+N, except at a line a multi-line script is still being composed on; Tab completes the filename/directory at the cursor against the panel's own current directory, or, when several matches agree on nothing further, opens a scrollable pick list of them instead of doing nothing). Every command runs through your actual $SHELL, started the same way an interactive login shell would be — that shell's own startup files, aliases and functions sourced first, so something like ll works exactly as it does in a real terminal — the same way Midnight Commander's own command line handles every command — no attempt to guess which programs need one and which don't. Its own output stays on screen until you press Escape to return, so it doesn't just flash by.

  • A middle row, always visible right below the command line, showing a curated subset of the keyboard layer's own letters as a quick legend — each key set off in its own petrol background, one space either side, so the letter-to-action mapping reads at a glance: Copy (c), Cut (x), Paste (v), Look (l), Edit (e), Execute (E), Move to Trash (d), Rename (r), Properties (i), Split view (s), Connect… (@), the tab switcher (t), toggle hidden files (. — labeled Hide or Unhide, whichever it would do next, not whichever state you're currently in), and Help (?), plus the six chord families marked with an ellipsis and no space after it to show they lead to more keys (g…goto, p…perms, m…menu, z…disp, o…opts, j…tools) — every member of an open chord's own legend is clickable too, the same highlighted-key treatment, so pointing at one works as well as typing its second letter. Details sidebar (I) doesn't have its own slot in this row any more — it was crowding it — but is still fully live from the keyboard and the header's own < button. A few of these change meaning while actually browsing the trash itself: d asks to remove permanently instead of moving something already-trashed to the trash again, r restores instead of renaming, and D empties the whole trash instead of removing just the selection — the button labels themselves stay put either way, since a bar that changed shape underfoot would be its own kind of confusing. Every entry here is also reachable from the context menu, whose own status-line legend follows the exact same pattern one level into a submenu, complete with a clickable ◂ Back button of its own. Every key is fully documented (including everything that doesn't fit this one row) in the in-app help (?), and each still works the same way whichever panel or field currently has focus, except while the command line itself is expanded and needs those same keys for its own editing. Hidden-files/size-format/mtime-format toggles are remembered across restarts.

  • A bottom row that's purely informational, no buttons on it at all: the current user (green, red while running as root), disk space (free/total, labeled with the real filesystem type — EXT4, CIFS, NFS4, ECRYPTFS, ... — the same one the Mounts screen shows) and inode usage (used/total, omitted on a filesystem that can't report a count at all) for the directory on screen — each with its own fixed color and a green/orange/red percentage (under 80% / 80%+ / 90%+) — git status (git:(branch) ⇡ahead ⇣behind +staged !unstaged ?untracked =conflicts, the same phrasing several zsh prompt themes already use, green/orange/red for clean/dirty/conflicted, shown only inside a git repository), the running kernel (uname -r), uptime and load average where the platform exposes them (Linux's own /proc/uptime and /proc/loadavg — quietly omitted elsewhere rather than shown wrong; load's own three numbers are colored against this machine's core count, not as raw, meaningless-alone figures), and a clock. Every one of those eight segments can be switched off on its own from Options → Status bar. See docs/user-guide.md for the full breakdown. The real filesystem root shows a selectable "/" row in place of the usual ..; selecting it and opening the Details sidebar (I) shows a much fuller version of this same overview — plus memory, swap, open file handles, mounted filesystems, process count, network interfaces and logged-in sessions, in the same colors and green/orange/red scale — while every other entry under "/" still gets its own ordinary per-file Details. On a remote connection's own "/" this describes that machine instead, sourced straight from its own /proc//etc/os-release over the same connection, no separate command-execution channel needed — except logged-in sessions, which does need one and is simply left out remotely. Selecting any other directory that's part of a git repository adds that same git status line as its own section there too, fetched a moment after the cursor settles and cancelled if it moves on, so scrolling through a long list of directories never queues up one git status per row. See docs/user-guide.md for the full field list.

  • Remote connections (SFTP): a compact @ button sits right before the path itself in the header — muted for a plain local panel, a slow pulse toward a lighter green and back once connected (never dipping toward black — that reads as "still searching for a signal", not a settled, already-alive connection) — opening a dropdown (also reachable via the plain @ key, or the g chord's own gc), styled and shaped like the tab switcher: + New connection plus recent history, each row its own independently clickable cells rather than markup-colored text — bright green for the one active in this panel, a matte dimmer green for one that has connected successfully before but isn't active now, red for one that used to work and just failed, plain/uncolored for one saved but never actually dialed yet (see the Connect dialog's own "OK" button below — a connection that failed its very first real attempt isn't added to history at all, unlike one saved without ever trying). The active row's own trailing ⏏ (or the "e" key) disconnects, right next to the ✕ (or "x"/Delete) every row already has to drop it out of history — every other history row carries a ✎ in that same spot instead ("e" there too), opening the Connect dialog prefilled from it without immediately retrying, so a saved Host/Port/User can be fixed before reconnecting. Every row is a real User | @ | Host/IP column table, not one merged string — User is the one keyboard stop for the whole label group (clicking anywhere across @/Host/IP still connects too). The Connect dialog itself has three buttons — Cancel/OK/Connect — with OK as the default (Enter in any field runs it): it only ever saves the typed fields into history, never dials, so several connections can be prepared ahead of the servers behind them actually existing. Opening via "+ New" or ✎ shows OK; clicking a history row's own label skips the buttons entirely and dials immediately, same as before. Editing (✎) shows only Cancel/OK — no way to dial from there. Saving edited Identifier/Tags through OK never moves the entry or touches its real green/red status; saving under a changed Host/Port/User creates a new, separate, plain-colored entry instead of renaming the old one. An optional "Identifier" field on the Connect dialog tells apart several similarly-named connections that otherwise only differ by host; a second, optional "Tags" field (comma-separated, up to 10, each up to 32 characters) adds a set-valued way to group and filter connections — normalized (trimmed, deduplicated, sorted) once the field loses focus, never while still typing. </> on the dropdown's own title bar adds both as a fourth and fifth column and spans the whole terminal width instead of just the active panel, remembered across restarts (connection_menu_expanded). Each column has a label above it (User/Host/IP, Identifier too once expanded) — click one, or U/H/I while the dropdown has focus, to sort by it (a second click/press of the same one reverses it), shown with an ↑/↓ suffix; the header itself is mouse-only, not reachable by arrow keys, and sorting resets to "most recently used first" the next time the dropdown opens. Tags has no mnemonic and isn't sortable, only filterable. An always-present "Filter:" field fills the rest of the top row — click it, press /, or Tab/Shift+Tab from the list (the dropdown's only two real keyboard stops, so either direction reaches it); Escape, Enter, or Tab/Shift+Tab again returns to the list without clearing what was typed (Escape again closes the dropdown). Narrows the list live across User/Host-IP (and Identifier/Tags once expanded) using the same AND/OR/NAND/NOR/NOT expression grammar as Log Holepunch's own keyword field; resets empty the next time the dropdown opens, same as sorting. Connecting dials in the background with a live progress line; authentication tries an ssh-agent first, then the usual default key files (~/.ssh/id_ed25519, id_ecdsa, id_rsa), then a typed password as the last resort. Host keys are checked against the real ~/.ssh/known_hosts — an unknown host raises a trust-on-first-use prompt, exactly like a real ssh client's own "authenticity of host" question, while a host whose key changed is always rejected outright with no automatic bypass — the dialog shows both fingerprints and offers a "Connect anyway" retry that skips the check for that one attempt only, never saved to known_hosts. Once connected, the panel browses the remote filesystem exactly like a local one — same columns, same sorting, same navigation, and Details/status-bar Disk+Inodes/System Info at "/" all describe the remote machine, not this one. Rename, permanent delete, chmod (including its recursive dirs/files options), and Copy/Cut/Paste all work against a remote target too, through the exact same Overwrite/Merge/Skip conflict dialog a purely local Paste already has, rather than an older engine that just refused outright the moment a destination already existed; "d" (Move to Trash) explains why it switches straight to a permanent-delete confirmation instead, since a remote session has no trash to move into. Look and Edit stage a real local temp copy behind the scenes and hand it to the ordinary built-in viewer/ external pager/$VISUAL/$EDITOR unchanged — Edit only uploads it back if it actually changed. Opening a zip/tar that itself lives on a remote connection downloads and browses it the same transparent way; above a configurable size (Options → Remote connections, KB/MB/GB, 10MB by default) it asks first, naming the real size. Copying a member back out of a remote archive works too, straight from the local temp copy already downloaded to browse it — to a local destination directly, or to another remote directory via a local staging round trip. Still refused with a clear message for now: chown, Compare, Batch rename, Sed Replace, and Properties as a whole. See docs/user-guide.md for the full picture.

  • Color schemes: JSON files under colorschemes/ in either config tier (see below), switchable live from the Options screen (the o chord's own oo) — no restart needed, and the pick is remembered for next time.

  • Search (f): by file name (glob, a plain keyword, or regex — via find, or locate where its own index is available) or by file content (grep, and — where installed — zgrep/zipgrep for gzip/zip contents), scoped to any directory, with real-time streamed results you can jump straight to. Runs the real system tools rather than a reimplemented search, so it inherits whatever's already indexed by locate's own updatedb. The results view isn't a dead end either: its own header carries a real, fully clickable/editable path breadcrumb — starting at the search's own scope — right alongside the status line, so you can keep browsing normally without first jumping to a specific hit or backing all the way out with Escape.

  • Look (l, the bottom bar's own button, or the context menu): a read-only, full-screen preview of the selected file's content. Plain text, source code, config files, diffs/patches, and logs get built-in syntax coloring (~200 languages, no external dependency — large files show their own first 8 MiB rather than being fully loaded). PNG, JPEG, GIF, BMP, TIFF, and WebP images render right in the terminal — decoded and scaled entirely in Go, no external tool needed there either. A format Look doesn't have a decoder for at all (HEIC, AVIF, RAW, ...) still opens the same overlay, with a recommendation for an external tool that can (chafa, or pixterm if you'd rather stick to Go-only tooling) — matched to whichever package manager your system actually has. Set pager = external in your config instead (see Color schemes below for the file itself) to open bat/batcat, $PAGER, or less/more in your real terminal for text. "Tail -f", right next to Look in the context menu, follows a growing log live via the real tail -f. PDFs open page by page — as a real rendered page image where poppler-utils's pdftoppm is installed (feeding straight into the same image renderer above), or as extracted plain text otherwise, entirely in Go, no external tool required. PageUp/PageDown move between pages either way; g/t switch a given page between rendered-image and extracted-text view on demand — handy since a text-heavy page rendered as an image downsamples into illegible mush at any realistic terminal size. A video file (MP4, MKV, WebM, AVI, MOV, and the rest of the usual container formats) hands the real terminal over to mpv instead, for full-screen playback — no graphical window needed, mpv falls back to its own true-color terminal renderer over SSH/tmux/screen. q or Ctrl+C quits back into breakthrough. The Details sidebar shows the same video as a still-frame thumbnail, grabbed via mpv 10% into its own duration to skip past a typically black/blank opening.

  • Archive browsing: Enter on a .zip, .tar, .tar.gz, .tar.bz2, .tar.xz, .7z, or .rar file opens a browsable listing of its own contents right in the panel, exactly like a real directory — .. steps back out to the real one it lives in. Mark entries with Space and Copy (c) them out to a real destination with Paste (v), nested folders and all. zip and tar are read entirely in Go, no external tool needed; 7z and RAR shell out to the real 7z and unrar binaries instead — neither format has a pure-Go story this project is willing to depend on (7z: no actively maintained, complete reader; RAR: a proprietary format with no legally clean Go implementation at all). Read-only otherwise, by design: renaming, editing, chmod/chown, Cut, and Move to Trash/Remove are all refused while browsing inside one. An archive found inside another is never opened this way automatically — it stays a plain file, extracted whole if you copy it out.

  • CSV/TSV tables: Look renders a .csv, .tsv, or .tab file as a plain, fixed-width-aligned table instead of its own raw delimited text — each column padded to its own widest cell, the same shape the POSIX column -t command already produces. Falls back to ordinary syntax-highlighted text if a file merely named that way isn't actually parseable as delimited data.

  • Bare .gz/.xz files: Look decompresses one to a temp file first, then shows it exactly as if it had never been compressed — a rotated log (syslog.1.gz, kern.log.4.xz, ...) opens the same way its own uncompressed, currently-active version already does. A .tar.gz/ .tgz/.tar.xz/.txz is left alone (it's a real multi-file container, already browsable by entering it like a directory — see Archive browsing above); decompressed content that isn't actually text still gets Look's own ordinary "no viewer for this file type" response.

  • Activity log: off by default, turned on under Options with a single log_level dial — errors, actions, detailed, or debug, each a superset of the one before it — plus eight independent on/off categories (file operations, permissions, archives, Sed Replace/Batch Rename, Rsync, remote connections/transfers, shell, firewall rules) so you can dial detail up or down per subsystem rather than all at once. Plain, greppable text, one line per entry, written to /var/log/breakthrough/breakthrough.log where that's writable or the same per-user directory the crash log already uses otherwise — actions records every real, state-changing action (Copy, Rename, chmod, Compress, a Rsync run, an SFTP connect…), enough to answer "what did I actually do with breakthrough" after the fact, and the foundation a future Undo will build on. An Action Log screen (jl) browses the real log file itself — newest entries first, with a keyword filter (full text over the message) and a time-range filter reusing the panel's own Modified-time filter syntax.

  • Log Audit (jL): reads other log files on disk — whichever directory the active panel currently shows, no arguments or flags. Discovers logrotate families (access.log, access.log.1, access.log.2.gz, ...), lets you check which ones to include, then merges them into one chronological stream with automatic format detection (JSON Lines — including Docker's json-file log driver, syslog RFC 3164/5424, a generic "timestamp level message" line, or plain text as a fallback), transparent .gz/.xz/.zst/.bz2 decompression, a live keyword filter — a boolean query (AND/OR/NAND/ NOR/NOT, parentheses for grouping, e.g. keyword1 and (keyword2 or keyword3)) over Source and Message, not just a flat substring — with match highlighting, a Time field (same syntax as the Action Log) and a minimum-severity Level field alongside it, a mini-timeline showing event density colored by severity, a Statistics overlay (counts by level and top sources), "e"/"w" to jump between errors and warnings, a follow mode ("f") that re-reads every two seconds, and a raw-line section in the detail view whenever the original line differs from the extracted message.

  • Git breach (jg): a multi-pane dashboard for the git repository containing the active panel's own current directory, shelling out to the system's own git rather than reimplementing it. Status (branch, ahead/behind, a one-line summary), Files (staged/unstaged/ untracked, conflicts first, syntax-colored diffs — Space stages/ unstages the row under the cursor), Branches (every local branch, checkout on Enter, delete with "d"), Commits (newest first, colored by author, a lane/branch graph for merges, loads 200 at a time) and Stash (apply with "a", drop with "d") are all live. The Main pane on the right shows whichever side box currently has focus — Files' own diff, an untracked file's raw content, or a Commits/Stash row's full diff — and can take real keyboard focus itself (Right/Enter/Space to jump in, arrow keys or PgUp/PgDn to scroll, Left/Enter/Space/Esc to step back out), remembering each file/commit/stash entry's own scroll position for the whole session. "c" commits whatever is currently staged. Local working trees only — there is no command-execution channel to run git against a remote session.

Status

Actively developed and usable day to day. Everything described above is built and tested: browsing, tabs, split view, the trash, Find, Look, archive browsing, Sed Replace, Batch rename, Compare, a full Options screen covering every setting breakthrough recognizes, a Toolbox screen of built-in networking and hardware tools with its own Network Tools (jn) and Hardware Tools (jh) screens, a Mounts screen (jm) showing what's currently mounted and whether it survives a reboot, a Firewall screen (jf) showing this host's own actual firewall rules (UFW, nftables, or iptables — whichever one really governs traffic), including which rules shadow each other, plus an Add-rule form (a, UFW/iptables only) that builds the exact command for a new rule, confirms it before running it via sudo, and automatically reverts an SSH-relevant rule if it isn't explicitly kept within 30 seconds, a Simulate form (t, every backend including nftables) that reports which rule, if any, decides a hypothetical request, an Action Log screen (jl) browsing the real activity log with keyword/time filtering, a Log Audit screen (jL) reading and merging other log files found in whichever directory the active panel currently shows, a Git breach dashboard (jg) for staging, committing, and diffing the local git repository containing the active panel's own current directory — its own diff pane toggles between a unified pager and a side-by-side view (v) — a Sessions screen (js) listing local GNU screen, tmux, and Zellij sessions side by side with per-row Attach/Close actions (Attach hands the real terminal to the session — screen -D -r, tmux attach -d, or zellij attach — and returns to breakthrough automatically once you detach or it ends), an SSH Keys screen (jk) inventorying local key pairs under ~/.ssh — algorithm/size, passphrase protection, file permissions, and whether each is currently loaded in the running ssh-agent — plus a Generate key form (a) that builds the exact ssh-keygen command for a new pair and runs it through a real terminal, deliberately with no passphrase field of its own (ssh-keygen prompts for one interactively instead, so it never sits in a process argument list or shell history), a Copy to server form (c, the same end result ssh-copy-id produces — never a duplicate, existing authorized_keys backed up first, strict permissions set explicitly — followed automatically by a non-interactive check that the new key alone now actually works passwordless), real Compress/Extract (jc/je/jE) through zip, tar, gzip, bzip2, xz, zstd, 7z, and (extract-only) RAR, and a notification system for background jobs finishing while you're looking elsewhere (a backgrounded Rsync run, the paste/move queue, the Firewall screen's own self-lockout rollback) — a transient status-bar toast, a persistent unread badge, and a full Messages screen (gm) with dwell-based auto-read, persisted across restarts as plain JSON, and an Email launcher (ge) for whichever terminal mail client is installed (neomutt, aerc, himalaya, mail, or mailx — configurable under Options when more than one is present), plus a status-bar mail badge ("Email") showing an unread count from a configured Maildir or mbox mailbox, or the current user's own system mailbox (/var/mail), auto-detected. Progress bars for long-running file operations are what's planned next — see docs/whitepaper.md for the full concept and vision, and follow along or join in on Discussions.

Architecture

The current package and function relationships are shown in the generated callgraph below. It is regenerated with go-callvis for changes merged into develop and for tagged releases.

breakthrough callgraph

Color schemes

breakthrough ships with one built-in scheme ("Default") and reads further ones from colorschemes/*.json in either config tier — /etc/breakthrough/colorschemes/ for system-wide schemes, ~/.config/breakthrough/colorschemes/ (or $XDG_CONFIG_HOME/breakthrough/colorschemes/ if set) for your own; a user file with the same name replaces a system one. Switch between whatever's found via the Options screen (the o chord's own oo) — the pick applies immediately and is saved to your own ~/.config/breakthrough/config.

The .deb/.rpm packages (see Installing) create /etc/breakthrough/config and /etc/breakthrough/colorschemes/ for you — the config file already fully documented and commented out, ready for a system administrator to uncomment and edit — so a package install gives you a real, discoverable starting point rather than a path that simply doesn't exist yet. A plain .tar.gz install has no installer to do that for it; the Options screen's own "Edit config file" button creates your user config the same way, on first use, if you'd rather start from that tier instead.

Two ready-made examples ship in examples/colorschemes/ — a dark Solarized-based scheme and a light one:

mkdir -p ~/.config/breakthrough/colorschemes
cp examples/colorschemes/*.json ~/.config/breakthrough/colorschemes/

A scheme file only needs to set the fields it actually wants to change — anything left out falls back to the Default scheme's own value. Each color is either a #rrggbb hex value or a W3C color name (e.g. "darkslategray"). See examples/colorschemes/solarized.json for every field a scheme can set. The semantic UI roles are surface_background for normal content, popup_background for menus and dropdowns, input_background/input_focused_background/ input_disabled_background for input states, selection_background for selected rows, and button_background/button_focused_background for buttons. text_color, muted_text_color, and border_color provide the corresponding foreground and chrome roles. The older accent_background, focused_background, editable_background, text, and placeholder_text fields remain supported as compatibility fallbacks for existing schemes.

Installing

Every release ships prebuilt binaries for all supported platforms on the Releases page. No runtime dependencies: breakthrough is a single static Go binary built with CGO_ENABLED=0, so there is nothing to install alongside it and nothing to break on a libc upgrade.

Which build do I need?

Your system Architecture Download
Linux, ordinary PC/server x86_64 / amd64 breakthrough_<version>_linux_amd64.*
Linux, Raspberry Pi 4/5 (64-bit), ARM servers, AWS Graviton aarch64 / arm64 breakthrough_<version>_linux_arm64.*
macOS, Intel x86_64 breakthrough_<version>_darwin_amd64.tar.gz
macOS, Apple Silicon (M1–M4) arm64 breakthrough_<version>_darwin_arm64.tar.gz
FreeBSD x86_64 / amd64 breakthrough_<version>_freebsd_amd64.tar.gz

Not sure which one you're on? uname -sm answers both questions at once — Linux x86_64 means linux/amd64, Linux aarch64 means linux/arm64, Darwin arm64 means Apple Silicon.

32-bit builds (i386, armv6/armv7) are deliberately not published. If you need one, it cross-compiles from source in one command — see Building from source below.

Debian, Ubuntu, Linux Mint, Raspberry Pi OS (.deb)

VERSION=0.18.0                     # or whatever the latest release is
ARCH=$(dpkg --print-architecture)  # amd64 or arm64
curl -LO "https://github.com/jagottsicher/breakthrough/releases/download/v${VERSION}/breakthrough_${VERSION}_linux_${ARCH}.deb"
sudo apt install "./breakthrough_${VERSION}_linux_${ARCH}.deb"

apt install ./file.deb rather than dpkg -i so any dependency resolution is handled for you. Upgrade by installing a newer .deb the same way; remove with sudo apt remove breakthrough.

Fedora, RHEL, AlmaLinux, Rocky, openSUSE (.rpm)

VERSION=0.18.0
ARCH=$(uname -m)                   # x86_64 or aarch64
case "$ARCH" in x86_64) PKG=amd64 ;; aarch64) PKG=arm64 ;; esac
curl -LO "https://github.com/jagottsicher/breakthrough/releases/download/v${VERSION}/breakthrough_${VERSION}_linux_${PKG}.rpm"
sudo dnf install "./breakthrough_${VERSION}_linux_${PKG}.rpm"   # or: sudo zypper install ./...

Both packages also install brk as a short alias for breakthrough — the same binary, invoked under either name.

Both packages also create /etc/breakthrough/config — fully documented, every setting listed and commented out — and /etc/breakthrough/colorschemes/, so a system administrator has a real starting point for machine-wide defaults (see Color schemes). Your own edits to that file survive a package upgrade: it's registered as a conffile on Debian and %config(noreplace) on RPM.

Any Linux, macOS, or FreeBSD (.tar.gz)

Works on any distribution, with or without root:

VERSION=0.18.0
OS=$(uname -s | tr '[:upper:]' '[:lower:]')          # linux, darwin, freebsd
case "$(uname -m)" in
  x86_64|amd64) ARCH=amd64 ;;
  aarch64|arm64) ARCH=arm64 ;;
esac

curl -LO "https://github.com/jagottsicher/breakthrough/releases/download/v${VERSION}/breakthrough_${VERSION}_${OS}_${ARCH}.tar.gz"
tar xzf "breakthrough_${VERSION}_${OS}_${ARCH}.tar.gz"
sudo install -m 0755 breakthrough /usr/local/bin/breakthrough
sudo install -m 0644 breakthrough.1.gz /usr/local/share/man/man1/
sudo ln -sf breakthrough /usr/local/bin/brk

Without root, drop it somewhere on your own PATH instead:

mkdir -p ~/.local/bin && install -m 0755 breakthrough ~/.local/bin/
mkdir -p ~/.local/share/man/man1 && install -m 0644 breakthrough.1.gz ~/.local/share/man/man1/
ln -sf breakthrough ~/.local/bin/brk

(The .deb/.rpm packages install the man page and the brk alias automatically — no separate step needed there; see man breakthrough once installed.)

On macOS, Gatekeeper quarantines anything downloaded with a browser. If you get "cannot be opened because the developer cannot be verified", clear the quarantine flag once:

xattr -d com.apple.quarantine /usr/local/bin/breakthrough

Downloading with curl, as above, avoids that entirely.

Verifying a download

Every release includes a checksums.txt covering all its artifacts:

curl -LO "https://github.com/jagottsicher/breakthrough/releases/download/v${VERSION}/checksums.txt"
sha256sum --ignore-missing -c checksums.txt      # shasum -a 256 on macOS/FreeBSD

Building from source

Needs only a current Go toolchain — no C compiler, no system libraries:

git clone https://github.com/jagottsicher/breakthrough.git
cd breakthrough
go build ./cmd/breakthrough

Cross-compiling for another machine is a matter of two environment variables, which is also how to get an architecture the releases don't cover:

GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 go build ./cmd/breakthrough   # 32-bit Raspberry Pi
GOOS=linux GOARCH=386        CGO_ENABLED=0 go build ./cmd/breakthrough   # 32-bit x86
GOOS=openbsd GOARCH=amd64    CGO_ENABLED=0 go build ./cmd/breakthrough   # OpenBSD

See CONTRIBUTING.md for the full development setup, and docs/installation.md for terminal requirements, the files breakthrough creates on your system, and uninstall instructions.

Documentation

  • docs/user-guide.md — every feature in detail, the complete settings reference, and a full keyboard map. ? inside the application shows a condensed version of the same thing, always matching the build you're running.
  • docs/installation.md — terminal requirements, exactly which files land where, optional external tools, system-wide rollout for administrators, upgrading, uninstalling, and troubleshooting.
  • docs/whitepaper.md — the concept and the reasoning behind the project.
  • docs/code-index.json — a generated, deterministic source index with file hashes, a Merkle root, Go package imports, and declared symbols for tooling and architecture work.
  • CONTRIBUTING.md — local setup and the branch workflow.

Contributing

Contributions are very welcome — this is an ambitious project for one person alone. See CONTRIBUTING.md for local setup, the branch workflow, and code style expectations.

Support

The best way to support breakthrough is to participate and contribute. If you'd like to leave a tip instead, you can send a few Satoshis via the Lightning Network ⚡️ using the sponsor button above.

About

POSIX-friendly TUI file manager for the terminal, built around a real embedded bash shell — mouse-driven panels, context menus, and dialogs. Go, tcell/tview.

Topics

Resources

Contributing

Stars

3 stars

Watchers

2 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages