Two panes. Fast file work. Git changes at a glance.
Modal Commander (mc) is a keyboard-driven file manager for the Windows terminal, inspired by Yazi, Helix and Total Commander. Browse independent panes, copy and move files without stopping your work, and inspect changes without leaving your file manager.
- Two panes, your workflow. Independent tabs, history, selections, filters and sorting; move or copy tabs between panes.
- Keep browsing during transfers. Background tasks show progress, support cancellation, and let you undo completed reversible work.
- Works with Explorer. Copy and cut files through the Windows clipboard, then paste in either application.
- Find the right line. Search filenames and file contents, respect
.gitignore, and jump from a match to your viewer. - See what changed. Vibe mode presents a live Git change tree rooted at the repository; Compare aligns two files side by side with inline differences.
- Make it yours. Eight themes, configurable function-key tools, bookmarks and a PowerShell command prompt.
- Download a Windows archive from GitHub Releases.
- Extract it and run
mc.exefrom your terminal. No Go installation is needed for a release binary. - To run
mcfrom anywhere, add the extracted directory to your userPATH: open Edit environment variables for your account, edit Path, and add that directory. Open a new terminal after saving.
Open two directories side by side:
mc.exe C:\projects C:\downloadsWith no directory arguments, the left pane opens your working directory. The right pane restores its saved tabs, or starts empty if none are saved. A second directory argument overrides restoration; extra directories become left-pane tabs. Only right-pane tabs persist between sessions.
The terminal title follows the focused pane’s current directory, for example mc - C:\projects\mc. It returns to the previous title when mc exits.
Keys are case-sensitive: Y means Shift+Y. Sequences such as gg mean press the keys in order.
| Key | Action |
|---|---|
Arrows or h/j/k/l, Enter |
Navigate and open items |
Tab |
Switch panes |
Space / Insert |
Select an item and advance |
e |
Open selected items with their Windows default app |
Y / X |
Copy / move selected items to the opposite pane |
y / x, then p |
Copy / cut, then paste through the Windows clipboard |
w |
View background tasks |
s / f |
Search filenames and contents / filter the current tab |
v |
Browse the live Git change tree (inside a repository) |
Shift+D |
Compare the file under each pane's cursor |
F3 / F4 |
Open the configured viewer / editor |
gg / Ctrl+J |
Enter a path / jump to an item by typing |
F1 |
Read help; press F there to filter it |
q |
Quit |
Normal transfers choose unique names on collisions. P requests overwrite with confirmation. Delete is permanent; overwrites are not undoable. Other completed reversible work, including partial transfers, can be undone.
Running mc.exe directly cannot change the parent shell's directory. Set up this PowerShell wrapper once, then launch with m to cd to the focused pane's directory when you quit.
Create your PowerShell profile if needed and open it (use your preferred editor instead of Notepad if you like):
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $PROFILE) | Out-Null
if (-not (Test-Path -LiteralPath $PROFILE)) {
New-Item -ItemType File -Path $PROFILE | Out-Null
}
notepad $PROFILEPaste this function into the profile and save it. Make sure mc.exe is on PATH as described above.
function m {
$tmp = (New-TemporaryFile).FullName
try {
mc.exe -o -tf="$tmp" $args
$cwd = Get-Content -LiteralPath $tmp -Encoding UTF8
if ($null -ne $cwd -and $cwd -ne $PWD.Path -and
(Test-Path -LiteralPath $cwd -PathType Container)) {
Set-Location -LiteralPath (Resolve-Path -LiteralPath $cwd).Path
}
} finally {
Remove-Item -LiteralPath $tmp
}
}Open a new PowerShell session, or reload the profile in your current session:
. $PROFILEIf PowerShell blocks the profile or helper scripts, enable scripts for your account, then reload the profile:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
. $PROFILELaunch mc with either:
m
m C:\projects C:\downloadsBrowse to a directory, switch to the pane you want with Tab, and press q. Your calling PowerShell session is now in that pane's current directory, ready for your next command. Q (Shift+Q) quits without changing the shell directory. Quitting from an empty pane also leaves it unchanged.
t.bat is a shortcut for wt -d .: it opens Windows Terminal in the calling shell's current directory instead of relying on the terminal profile's default starting directory. Windows Terminal must be installed and wt available on PATH.
Release archives include t.bat. Keep it in the extracted directory added to PATH above, or download it from the scripts directory and add its containing directory to PATH. Then run:
m # Browse to a directory, then press q to cd there
t # Open Windows Terminal in that directoryIn mc, press gC to save the default configuration, then gc to open its directory. Edit $env:APPDATA\mc\config.toml to configure your F-key tools; put the tools you use on PATH.
For other helpers, including pp.ps1 to save clipboard images, see the scripts directory and setup instructions.
The file manager runs on Windows. External tools are optional and needed only for the features that invoke them:
| Tool | Used for |
|---|---|
git on PATH |
Git status, tally lists and Vibe mode |
koneko |
Default F3 viewer; line targeting in Search and Vibe |
hx (Helix) |
Default F4 editor |
code (VS Code) |
Default F7/F8 tools |
deps |
Default F2 dependency viewer |
lazygit on PATH |
Default F9 Git interface |
All F-key tools are configurable. bat with less can be configured as an alternative viewer. For the intended mouse and icon experience, use Windows Terminal and a Nerd Font such as JetBrainsMonoNL Nerd Font. Install the font, then select it in your Windows Terminal profile settings.
Two-pane workflows, selections and background tasks
mc now uses catatui, with two independent panes and background file tasks. Each pane has its own tabs, history, selection, filter and sorting. Existing themes and F-key tool configuration are retained.
Tabswitches panes. Click a pane to focus it; click a pane tab to switch directly.Ctrl+Left/Ctrl+Rightmove the current tab to that pane and follow it;Shift+Left/Shift+Rightcopy it there and keep the focus.- A pane may hold no tabs at all:
Ctrl+Wcloses the last one and a move can empty the source. The empty pane keeps its half of the screen;Trestores the last closed tab,ggopens a path,bpicks a bookmark, andShift+Left/Shift+Rightcopies one in from the other side. Keys that need a current directory do nothing there. Quitting from an empty pane returns no directory. Space/Inserttoggles an item and advances; select-all, invert and clear remain available. Visual mode has been removed.Shift+Up/Downextends or shrinks a range;Shift+Home/Endextends to the first or last item;Ctrl+clickextends to the clicked item. Earlier selections are preserved. Ordinary navigation starts a new range anchor.Shift+clickalso works in terminals that forward it, but Windows Terminal reserves it for terminal text selection.Ycopies andXmoves selected items to the opposite pane. Enter confirms the editable destination. Existingy/x/p/Pclipboard operations are unchanged.wopens tasks;ccancels the highlighted task and Escape returns to browsing. File operations run sequentially while browsing remains available. Progress shows bytes and file counts, with scanning shown before totals are known.Ctrl+Jenters Jump mode.Ctrl+Nstill opens a directory in a new tab.Shift+Dopens Compare mode for the file under each pane's cursor, ignoring marked selections.- Inside a git work tree each entry carries its status in a column before the name (
Mmodified,Aadded,Ddeleted,Rrenamed,Uconflicted,?untracked, ignored entries dimmed), and a directory takes the most severe state of anything inside it. The path row says where the repository is: the component the work tree starts at is highlighted — coloured while anything is uncommitted, green once nothing is — and the branch with its↑↓and+~?tallies sits at the end of the same row, stepping aside when the pane is too narrow for both. The tallies are clickable: point at~10to light it up and click for the list of exactly those files anywhere in the repository, whereenterjumps to one,F2-F12run their tools on it andEsccloses it;gm,guandgaopen the same three lists from the keyboard. It needsgitonPATH; elsewhere mc never even spawns it. Setgit = falseinconfig.tomlto turn it off.
Normal transfers choose unique names on collisions; P explicitly requests overwrite and confirms collisions. Cancellation retains completed files and removes unfinished temporary copies. Completed reversible work can be undone, including partial tasks. Delete is permanent; overwrites are not undoable. Undo/redo refuses conflicting or changed paths. Reparse points are reported as unsupported for transfers/deletion.
The right pane's tabs are saved to $env:APPDATA\mc\tabs.list on exit. The left pane and task history are not persisted.
Set a theme with g -> T, save with g -> C, or edit $env:APPDATA\mc\config.toml.
The main mode of the program, from which most other modes can be accessed.
q - Quit, returning the current directory.
Q - Quit without returning anything.
space - Select.
Shift+Up / Shift+Down - Extend or shrink the selection range.
Shift+Home / Shift+End - Extend the selection range to the first or last item.
Ctrl+click - Extend the selection range to the clicked item.
Ctrl+a - Select all.
Ctrl+d - Deselect all.
Ctrl+r - Toggle selection (invert all).
y - Copy selected items. This uses standard Windows file paths, so you can paste them directly into Explorer.
x - Cut.
d - Delete PERMANENTLY. It will prompt for confirmation.
r - Rename. When multiple items are selected, an editor opens so you can edit all the names at once.
p - Paste.
P - Paste with override. Prompts for confirmation if there's a collision.
u - Undo.
U - Redo.
t - Copy current tab.
Ctrl+w - Close current tab.
T - Restore closed tab.
Ctrl+n - Open selected directory in a new tab.
] - Next tab.
[ - Previous tab.
1-0 - Select tabs 1 to 10 (0 is tab 10).
Ctrl+b - Go back in history.
Ctrl+f - Go forward in history.
Directories update automatically from filesystem notifications, with periodic checks for missed changes and clipboard updates. Refresh keeps the focused file and scroll position when those items still exist. F5 forces an immediate update.
B - Bookmark the directory.
b - Browse bookmarks.
Can be entered by pressing Ctrl+J in the normal mode. Jump mode is to mimic Explorer's behavior when pressing buttons to jump to the needed item.
Compare: side-by-side file differences
Press Shift+D in Normal mode to compare the files under the left and right pane cursors. The read-only Differences view aligns lines side by side, with red/green changes and inline highlights. Marked selections are ignored. Both cursors must point to files.
Use n/p for the next/previous difference, j/k or arrows and the mouse wheel to scroll, PgUp/PgDn to page, Home/End for the beginning/end, and h/l or left/right to pan long lines. F5 reloads the same two paths; w opens Tasks. Esc or q returns to the panes with their state preserved.
UTF-8 text (including BOM) up to 5 MiB per file gets detailed comparison. Whitespace, line endings and missing final newlines remain significant. Binary files, unsupported encodings, larger files and comparisons exceeding the work or 100,000 line-break limit get a streamed identical/different summary. This is a snapshot; refresh with F5 after external edits.
Vibe: a live, repository-root Git change tree
Press v in Normal mode anywhere inside a Git working tree. Vibe opens a live tree rooted at the Git repository root, even when your pane is in a subdirectory. At 100 terminal columns or wider, directories, files and hunks appear on the left, and the selected file or hunk's diff appears on the right. Below 100 columns, Vibe uses the combined full-width view illustrated here:
repository/
src/
components/
[M] button.go +1 −1
Lines 12–14 +1 −1 in func render()
12 12 context
13 -old line
13 +new line
14 14 context
The tree contains only changed files and their parent directories. Files expand into diff hunks with three surrounding context lines. A hunk is labelled by the lines it spans in the current file, its added and deleted counts, and the enclosing function when Git can tell. Added lines are green, deleted lines red; the two line-number columns refer to the baseline and current file. In the wide view, selecting a file shows all its hunks on the right; selecting a hunk shows just that hunk. Selecting a directory prompts you to select a file. Folders and files start expanded.
Vibe compares the working files against HEAD, combining staged and unstaged changes. Edits that cancel out relative to HEAD have no net diff. Untracked files appear individually as additions; ignored files are excluded. Before the first commit, the baseline is empty. Git must be on PATH and git = true enabled in config.toml.
| Key | Action |
|---|---|
j/k, Up/Down |
Move through visible rows |
| Mouse wheel | Scroll the pane under the pointer without moving the selection |
Tab |
In the wide view, switch keyboard focus between the tree and diff |
PgUp/PgDn, Home/End |
Page or jump to the first/last row |
h/Left |
Collapse a branch, or select its parent |
l/Right |
Expand a branch, or enter it |
Space |
Toggle expansion |
e / c |
Expand all / collapse all branches |
[ / ] |
Previous/next hunk; expand its ancestors |
Enter/F3 |
View the selected file/change; Enter toggles directories |
F5 |
Refresh immediately |
w |
Toggle word wrap (on by default) |
Esc/q |
Return to the panes |
The repository root stays expanded. c collapses its descendants, leaving top-level files and folders visible.
Click selects a row. Double-click toggles a branch or views a line. F3 on a file or hunk opens its first change. Vibe only ever opens the current working file: added and context lines open at that line, and a deleted line opens where it used to be, at its replacement or the next surviving line. An entirely deleted file has nothing to open, and the status row says so. Koneko receives mc's theme and selects the requested line. Custom F3 viewers are honored, with line targeting available for koneko.
Automatic updates: Vibe refreshes every two seconds while visible, including changes to files, the index, branch and HEAD. Refreshes run in the background; repeated requests coalesce. The tree preserves expansion, selection and scroll position where possible. If the selected row disappears, selection moves to a surviving parent. A refresh failure retains the previous tree and retries. Polling pauses during external viewing and refreshes immediately on return. F5 requests an immediate refresh.
Long tree labels and diff text wrap to the available width. In the narrow view, continuation lines retain the outer tree branches and belong to the same selectable row. Mouse hover highlights the entire tree row, including its wrapped lines. In the wide view, navigation keys scroll the focused pane, while [ and ] select hunks from either pane. Clicking a pane focuses it.
Press w to toggle wrapping; in the wide view it controls the diff. Each wide pane has its own scrollbar, which supports clicking and dragging. Mouse wheel scrolling follows the pointer even when keyboard focus is in the other pane.
Vibe is read-only. Binary, oversized, conflicted, symbolic-link, submodule and metadata-only changes appear as summaries. Text previews are limited to 5 MiB per file; patches are bounded at 32 MiB and displayed diff lines at 100,000 per refresh. Current files remain viewable from summary rows. Vibe complements the Git tally lists (files grouped by status) and Compare mode (two cursor files from the panes).
Search and filter
Press s to search filenames and contents. Tab cycles focus. Search respects .gitignore by default (F1 toggles it) and is case-insensitive (F2 toggles it). F5 or Enter while focusing a text input starts the search.
F3 opens the selected match in the configured viewer, targeting its line when supported. n / N selects the next / previous match; h hides matched lines.
Entered by pressing f in the normal mode. Current tab can be filtered.
Copy paths, sorting, creation, shell and navigation modes
Entered by pressing c. Capital letters convert slashes from \ to /. "Copy the filenames as arguments" means it can be used as arguments for terminal commands (if path has spaces it will be quoted).
c/C - Copy the file path/Forward.
d/D - Copy the directory/Forward.
f - Copy the filename.
n - Copy the filename without extension.
a/A - Copy the file paths as arguments/Forward.
s - Copy the filenames as arguments.
q/Q - Copy the file paths as array/Forward.
w - Copy the filenames as array.
Entered by pressing , (comma). Capital letters sort in reverse.
m/M - Sort by modified time.
a/A - Sort alphabetically.
n/N - Sort normally.
e/E - Sort by extension.
s/S - Sort by size.
r - Sort randomly.
Entered by pressing a. If your name ends with a slash it's a directory.
Entered by pressing ` (backtick). The message history can be viewed here.
Press : to enter shell mode. You can hide and show TUI by pressing Ctrl+h to see the result of a command. #sl - is a macro that is converted to a list of selected items for a command.
Press ; in normal mode to rerun the last shell command without reopening shell mode. #sl expands against the current selection, so the same command can be applied to different items.
Ctrl+b - Back in history.
Ctrl+f - Forward in history.
Go mode is just a menu.
g - Enter Path mode.
t - Browse tabs.
T - Set theme.
c - Open the settings directory. You can also find and delete bookmarks there, for example.
C - Save settings to config.toml for editing.
s - Calculate size for the selected directories. Runs as a background task alongside file operations; watch or cancel it with w.
Press gg to enter path mode.
ctrl+u - Clear all left of cursor.
ctrl+k - Clear all right of cursor.
ctrl+w - Delete a word.
tab - Autocomplete.
up/down - Next/previous autocomplete.
ctrl+e - Expand environment variables.
ctrl+n - Open the path in a new tab.
Configuration, themes and F-key tools
Settings live in $env:APPDATA\mc\config.toml. Press gT to choose a theme, gC to save settings and gc to open the settings directory. Bookmarks and saved right-pane tabs live beside the config. Set git = false to disable Git integration.
Themes: dracula (default), autumn, base16, ferra, github, monokai, nord and tokyonight.
F2-F4, F6-F12 - tools. They can be configured in config.toml. The default config can be saved by pressing gC (g and then C, and then gc to find it).
F2 - Dependency walker. deps by default, but everything is configurable.
F3 - Viewer.
F4 - Editor.
F6 - Open the directory in Explorer.
F7 - Open the files in VS Code.
F8 - Open the directory in VS Code.
F9 - Open lazygit in the current directory (requires lazygit on PATH, configurable).
F10-F12 - Unassigned (configurable).
Requires Windows and Go 1.27.0. The released catatui dependency is fetched through the Go module proxy; no sibling checkout is needed.
.\build.ps1
.\build.ps1 dist # Build and package a release in dist/
.\build.ps1 icon # Embed the icon (requires rsrc)dist also runs build.ps1 in the sibling ..\deps and ..\koneko checkouts
before packaging their newly built executables. Both checkouts are required for
distribution builds; a missing checkout or failed tool build stops packaging.
This builds their current local sources; it does not fetch or pull Git updates.
Plain .\build.ps1 builds only mc.
If that doesn't work, you may need to enable PowerShell scripts first:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUserValidate changes with:
go test ./...
go test -race ./...
go vet ./...Version, Git commit and build time are embedded at build time. The build script
uses the nearest Git tag as the version (or the commit hash if no tag exists),
appending -dirty for uncommitted changes. Tag a release commit before building
its distribution, for example git tag -a v2.0.0 -m "Release v2.0.0".
Use mc.exe -version (or -v) to inspect the version. The -o and -tf path
flags support the optional shell wrapper's temporary-file output.
Legacy v1 screenshot gallery
These images show an earlier version, not the current two-pane interface.
Found a bug or have an idea? Open an issue. mc is available under the MIT license.






