Meridian Commander

The two-pane file manager
for everywhere your files live

A terminal file manager in the spirit of Midnight Commander, written in pure Python. Put the local disk in one pane and an SFTP, SSH or FTP server in the other — then copy, move, sync, view and edit as if it were all one machine.

The meridian is noon — the other end of the clock from midnight.

$ pip install "meridian-commander[ssh]"
Meridian Commander main view: two panes side by side, one on a local project and one on /srv/www, with two files tagged in yellow

Runs in any terminal · Python ≥ 3.9 · Linux & macOS · GPL-3.0 · zero required dependencies for local + FTP use

Features

One interface, any pair of locations

Every location — local disk, SFTP, SSH shell, FTP — implements one small filesystem interface, so the copy, move and sync engines are written once and work across any pair of panes: local→remote, remote→local, even remote→remote.

🗂

Two independent panes

Browse two locations side by side. Tab between them, swap with Ctrl-U, sort, tag files for batch operations, and toggle hidden files per pane.

🌐

Local + SFTP + SSH + FTP

Each pane can point at the local disk or a remote server. SSH-shell mode works even on servers with the SFTP subsystem disabled, driving ls, cat and friends over the SSH channel.

Bidirectional sync

One key compares both trees and copies the newest version of every file in whichever direction is needed. Full preview before anything is written, and nothing is ever deleted.

📄

Viewer & editor built in

A scrollable viewer with smart-case search and highlighted matches, and a real in-place editor with save on Ctrl-S — both work on remote files too.

💻

Terminal inside the pane

Press t and the pane becomes a shell in that pane's directory — a real pty locally, or an interactive shell over the pane's existing SSH connection.

🗜

Archives as directories

Press Enter on a .zip or .tar.gz and the pane goes into it. An archive is a read-only filesystem, not a viewer, so listing, tagging and copying files out all work exactly as from a directory.

🖋

Markdown, rendered

F3 on a .md drops the markers and uses real terminal attributes — bold, italic, reversed code — with headings ruled, lists laid out, tables aligned and links numbered. r shows the source.

📄

PDF, page by page

F3 on a .pdf rebuilds each page's text from where the glyphs landed — lines from baselines, spaces from the gaps, columns kept side by side. /ToUnicode CMaps, object streams and cross-reference streams are all followed. A scanned page has no text; press i and its image opens in the image viewer.

🖼

Images, in colour

F3 on a PNG, JPEG, GIF or BMP draws the picture. Half-block characters give two pixels per cell, so an 80×24 terminal is 80×48 square pixels, quantised to the xterm-256 palette. Pan, zoom, step GIF frames, or switch to an ASCII ramp. JPEG is decoded from DC coefficients alone — the image at 1/8 scale, with no inverse DCT at all.

📝

Word documents as text

F3 on a .docx shows the document wrapped to the terminal — headings ruled, lists indented, tables in aligned columns — and searchable like any other file. Standard library only.

📽

Slide decks, one at a time

F3 on a .pptx pages through the deck — title, bullets at their outline levels, tables, and t for the speaker notes. Slide order comes from the deck itself, not the file names.

📊

Spreadsheets in a grid

F3 on an .xlsx opens a full-screen grid — row numbers, column letters, aligned numbers, dates through the workbook's own formats, Tab between sheets. Read with the standard library alone, so it needs nothing installed.

🔍

Find files, browsably

Search a pane's tree by substring or glob — remote panes included — and get a result list you can view, edit or jump into directly.

🧩

Pane plug-ins

A plug-in takes over a pane and can see the opposite pane's filesystem. Writing one takes a dozen lines of Python; built-ins include an SSH JSON push and run-remote-script.

🖱

Mouse support

Click to select, double-click to open, wheel to scroll, right-click for a context menu of actions — alongside full keyboard control.

🪶

Pure Python, stdlib core

Local and FTP use run on the standard library alone. SFTP/SSH needs only the optional paramiko package. No compiled extensions, no framework.

Remote locations

If ssh mybox works, typing mybox here works

Your SSH config, understood natively

Meridian reads ~/.ssh/config. Enter a host alias or a user@host string, leave everything else blank, and it authenticates through your SSH agent and default keys — with HostName, User, Port and IdentityFile all applied.

  • Native, alias-aware ProxyJump — jump-host chains are resolved and tunnelled entirely inside the app, no external ssh process.
  • Restricted shells handled — file transfer falls back from cat to dd to the raw scp protocol, so viewing and editing work even on appliance shells that answer Command not supported.
  • Old FTP servers too — prefers modern MLSD listings and falls back to parsing classic LIST output.

Once connected, a remote pane behaves exactly like a local one.

The Open location dialog with an SFTP connection form, where a bare ssh-config host alias 'web01' is being entered
Synchronization

Both sides end up with the newest of everything

The Synchronize panes dialog showing a plan of ten files with per-file direction arrows and reasons before confirmation

A plan you approve, not a surprise

F9 walks both directory trees and builds a plan: files missing on one side are copied over, files present on both sides go in whichever direction the newer copy lives. You see every planned copy — with its reason — and the total byte count before confirming, and you can cancel mid-way.

  • Copied files are stamped with the source's modification time, so a second sync finds nothing to do.
  • Timestamps within 2 seconds count as equal — no needless copies across filesystems with coarse clocks.
  • A pane holding hundreds of files or dozens of subdirectories — a home directory, say — is queried before the walk starts, not after you have waited for it.
  • Nothing is ever deleted.

A shell without leaving the pane

Press t and the pane itself becomes a pseudo-terminal running a shell in the pane's directory, while the other pane keeps working normally. On an SFTP or SSH pane, the shell runs over the pane's existing SSH connection.

  • Ctrl-] switches to the other pane while the shell keeps running.
  • For full-screen programs like vim or htop, ! suspends the UI into a real terminal.
A shell running inside the left pane showing the output of a deploy script dry run, while the right pane still shows a file listing
The built-in file viewer on a Python source file with every match of a search highlighted and line numbers on

View and edit, wherever the file is

The viewer scrolls, searches with smart case (/, matches highlighted, n/N with wrap-around), toggles line numbers and wrapping. The editor is a real in-place editor — insert and delete, sane Enter/Backspace line handling, save with Ctrl-S. Both work on remote files: open a config on a server, fix it, save it back.

Press F3 on an .xlsx or .xlsm and you get a grid instead: row numbers, column letters, numbers right-aligned, dates rendered through the workbook's own number formats, Tab between sheets and the same / search. A workbook is a zip of XML, so it is read with zipfile and xml.etree — nothing to install, and a spreadsheet on a remote pane opens like any other file. Legacy .xls is a different format entirely and stays in the text viewer.

A .docx opens the same way, as text: headings ruled, bulleted and numbered lists indented by depth, tables laid out as aligned columns, wrapped to whatever width the terminal happens to be. A list marker comes from the document's own numbering.xml — in a real Word file a bullet and a numbered item are both style ListParagraph, and nothing else tells them apart.

Plug-ins

A pane that runs your code

Press p to put a pane into plug-in mode. The plug-in takes over the pane and can see the opposite pane — its filesystem (local or remote), its directory, its entries — so it can act on whatever you have open next to it. A complete plug-in is a dozen lines in ~/.config/meridian-commander/plugins/:

from meridian_commander.plugin_api import InputOutputPlugin

class Shout(InputOutputPlugin):
    name = "Shout"
    description = "Uppercase whatever you type"
    prompt = "say> "

    def process(self, line):
        return [line.upper()]

InputOutputPlugin gives you the classic layout — scrolling output above, input line below — and self.ctx exposes the opposite pane (other_fs, other_path, other_entries()). Subclass PanePlugin instead for full control of drawing and keys. Built-ins include the in-pane terminal, find-in-other-pane, an SSH JSON push, and run-remote-script.

Screenshots

More of the app

Key bindings

Muscle memory included

The classic function-key row does what it has done since 1986 — and every F-key has a digit alias (10) plus a mnemonic letter, for terminals and multiplexers that swallow function keys. A stray Esc never quits the app.

Function keys

F1 / ?help
F2 / oopen / connect location
F3 / vview (.md / .xlsx / .docx / .pptx)
F4 / eedit file
F5 / ccopy to other pane
F6 / mmove to other pane
F8 / ddelete
F9 / ssynchronize panes
F10 / qquit

Panes & navigation

Tabswitch active pane
/ jkmove cursor
Enter / enter directory or archive / view file
Backspace / parent directory
Insert / Spacetag file (+/- all / none)
Ctrl-Uswap panes
Ctrl-Ggo to path
~home directory of this pane
=other pane: same directory & connection
bpresets: saved locations
.show / hide hidden files

Power tools

tterminal inside this pane
!full-screen shell (vim, htop, …)
ffind files, browsable results
p / F11plug-in mode for this pane
Cconfiguration menu
Ctrl-Tchange sort order
Ctrl-Rreload panes
Right-clickcontext menu of actions
Install

Running in under a minute

01

Install

pip install "meridian-commander[ssh]"

The [ssh] extra pulls in paramiko for SFTP/SSH panes. Plain pip install meridian-commander is enough for local and FTP use — it runs on the standard library alone.

02

Launch

meridian-commander /etc /var/log
meridian          # short alias

Optionally pass a starting directory for each pane. With no arguments the left pane opens in the current directory and the right one at your home directory.

03

Connect

F2  →  SFTP  →  mybox

Type an ~/.ssh/config alias or user@host and leave the rest blank — agent, keys, ProxyJump and all are applied, just like the ssh command.

From a checkout: pip install -e ".[ssh]" for development, or run it straight from the source tree with python -m meridian_commander. Tests run with pytest.