A keyboard-driven, dual-panel file manager for the terminal, written in Rust (ratatui + crossterm). It recreates the look, layout, and workflow of Norton Commander 5.5 (1998) — blue/cyan panels, double-line frames, a function-key command bar, and an always-available command line — with a small set of modern extras layered on top: quick filter, fuzzy directory jump, panel tabs, git-aware panel info, mouse support, and switchable themes.
Platform: Windows-first (Windows Terminal / PowerShell console / conhost).
The codebase stays cross-platform via crossterm, but only Windows is tested
and supported.
- Installing
- Building from source
- Command-line options
- Configuration
- Using FileCommand
- Project layout
- Testing
- Contributing
- Reporting issues
- License
The easiest way to get FileCommand on Windows is the bundled installer:
-
Build (or download)
FileCommandSetup.exe— seeinstaller/README.mdfor build instructions, prerequisites (Rust, .NET SDK, WiX v4/v5 CLI), and a winget manifest. -
Run it. By default it installs per-user, with no elevation, to
%LocalAppData%\Programs\BigHatGroup\FileCommand, adds itself to your userPATH, and creates a Start Menu shortcut. -
Open a new terminal window (existing ones won't see the updated
PATH) and run:filecommand
For an elevated, machine-wide install instead:
FileCommandSetup.exe /quiet InstallScope=perMachineSilent uninstall:
FileCommandSetup.exe /uninstall /quietSee installer/README.md for the full scope
semantics (per-user vs. per-machine, upgrade behavior, production code
signing) and the winget package template.
Requires the Rust toolchain (stable).
git clone https://github.com/kkaminsk/FileCommand.git
cd FileCommand
cargo build --releaseThe binary is produced at target\release\filecommand.exe. Run it directly,
or via Cargo:
cargo run --releaseThe workspace has two crates:
filecommand-core— state, the reducer (core::update), file operations, listing/sorting/filtering, git info, config/theme loading. No terminal dependencies; fully unit-testable.filecommand-tui— thefilecommandbinary: the event loop, rendering, input mapping, and terminal ownership.
| Flag | Effect |
|---|---|
--theme <name> or --theme=<name> |
Launch with a specific theme for this session, overriding the saved theme = in config.toml. Built-in names: nc-classic, nc-mono, terminal-green, purple-lights, yellow-storm, inverted. |
--nosplash |
Skip the startup splash screen, overriding splash = true in config.toml. |
--nomouse |
Disable mouse capture for this session, overriding [mouse] enabled in config.toml. |
Flags can be combined in any order, e.g. filecommand --nosplash --theme=purple-lights.
FileCommand reads and writes its files in the current working directory (the directory it's launched from), not a fixed profile directory. Missing files are created with defaults on first run; a malformed file produces a startup warning and falls back to defaults rather than being silently overwritten.
Flat key = value lines (not a full TOML parser, aside from the [mouse]
table below), one setting per line:
splash = true
theme = "nc-classic"
shell = "cmd.exe /C"
editor = "code -w"
panel_split = 50
[mouse]
enabled = true
key.paste_name = "ctrl+enter"
key.paste_path = "ctrl+]"
key.quick_filter = "ctrl+p"
key.fuzzy_jump = "ctrl+j"
key.split_left = "ctrl+left"
key.split_right = "ctrl+right"
key.split_reset = "ctrl+="
key.clipboard_files = "ctrl+c"
key.clipboard_paths = "ctrl+shift+insert"| Key | Meaning | Default |
|---|---|---|
splash |
Show the startup splash screen. | true |
theme |
Active theme name (built-in or a file in themes/). |
nc-classic |
shell |
Shell command line used by the file-action menu's Run entry and the F2 user menu. | cmd.exe /C on Windows, /bin/sh -c elsewhere |
editor |
External editor command for F4; unset means "use the built-in editor". | unset |
panel_split |
Left-panel width as a percentage. | 50 |
[mouse] enabled |
Whether mouse capture is enabled at all. | true |
key.* |
Overridable key bindings (see table below). | see below |
Only the bindings listed above are configurable; the rest of the key map (F-keys, Tab, arrows, Ctrl+T/W, etc.) is fixed. Unknown keys are ignored rather than rejected, and the schema tolerates missing/malformed lines.
User-defined themes. Each theme maps the role names documented in the design
spec (panel.frame, panel.cursor, dialog.primary, ...) to ANSI-16 color
names (black, red, green, yellow, blue, magenta, cyan, white,
and bright- variants), with an optional per-role #RRGGBB truecolor
override. Switch themes at runtime via Options → Themes, or pin one at
launch with --theme.
F2 user-menu entries — a real TOML array of tables:
[[entry]]
label = "Open command prompt here"
command = "cmd.exe"
[[entry]]
label = "Directory listing"
command = "dir"Unlike config.toml, a malformed usermenu.toml is treated as an error: it
falls back to a default menu and shows a warning rather than being silently
overwritten.
Command-line history and fuzzy-jump directory frecency data, written atomically so a crash mid-write never corrupts it. Not meant to be hand-edited.
- Left navigation pane — its own directory, cursor, selection, sort mode, filter, and display mode; the path renders inverse in the top border when this panel is active.
- Right navigation pane — independent of the left: its own mode, sort, filter, and tabs, so the two panels can browse entirely different locations at once.
- F-key menu — the function-key command bar (
1Help 2Menu 3View 4Edit 5Copy 6RenMov 7Mkdir 8Delete 9PullDn 10Quit); clickable with the mouse, and relabels under Ctrl/Alt for their key-bar variants.
- Two independent panels, each with its own directory, cursor, selection, sort mode, filter, and display mode.
- Tab switches which panel is active (the active panel's path renders inverse in its top border).
- The command line sits below the panels and always shows the active
panel's path as its prompt. It understands three built-in verbs (
cd,del,rmdir) and nothing else — see Command line. - Terminal is usable down to a minimum size; below that, panels are replaced with a "terminal too small" placeholder and the F-key bar degrades through progressively shorter forms as width shrinks.
- The startup splash (product name/version banner) is the very first frame
unless
--nosplash/splash = false; any key dismisses it early.
The line just above the F-key bar is an NC-style command line. Its prompt is
the active panel's current directory (e.g. C:\Projects\app>), and it
follows the active panel: press Tab or change directory and the prompt
updates.
The command line is a quick way to navigate and to delete a single item by
name. It is not a shell: it never passes text to cmd.exe/PowerShell,
and anything other than the three built-in verbs below is rejected. To run
programs, see Running programs.
You don't need to focus the command line. While the panels have the keyboard (no dialog, menu, type-ahead jump, or Ctrl+P quick filter open), every plain printable key is added to the command line and the panel cursor stays put. Text is only ever added at the end; there is no cursor to move within the line.
Some keys mean different things depending on whether the line is empty:
| Key | Line empty | Line has text |
|---|---|---|
| Printable character | Starts the line | Added to the end |
+ / - / * |
Select / deselect by wildcard, invert selection | Typed as text |
| Backspace | Go to the parent directory | Delete the last character |
| ↑ / ↓ | Move the panel cursor | Recall older / newer history |
| Enter | Act on the cursor entry (open a directory, file-action menu for a file) | Run the line |
| Ctrl+Enter / Ctrl+] | Paste the cursor entry's name / full path | Same, after a separating space |
| Esc | Ask to quit | Ask to quit (cancelling keeps your text) |
All other keys keep their panel meaning while you type. Home/End and PgUp/PgDn move the panel cursor, and Tab switches panels without clearing the line. Delete opens the delete confirmation for the cursor entry; it does not delete a character. To clear the line, backspace it until it's empty.
| Command | What it does |
|---|---|
cd <path> |
Navigates the active panel to <path>. If the target doesn't exist or isn't a directory, the command is rejected and the panel stays where it is. |
del <file> |
Opens the normal F8 delete confirmation for that one file. Rejected if the target is a directory. |
rmdir <dir> |
Opens the normal F8 delete confirmation for that one directory, including the second confirmation if it isn't empty. Rejected if the target is a file. |
Rules that apply to all three:
-
Verbs ignore case.
CD,Cd, andcdare the same command. -
Everything after the verb is one argument, so a name with spaces doesn't need quotes. Surrounding double quotes are removed, so
cd Program Filesandcd "Program Files"both work. -
Paths resolve against the active panel's directory. These forms are accepted:
Form Example Resolves to Relative cd src\core,cd ..\otherBelow or beside the current directory Current / parent cd .,cd ..Current directory / its parent ( cd ..at a drive root is rejected)Drive-rooted cd \Temp\Tempon the current driveAbsolute cd C:\Windows\System32That exact path Bare drive letter cd D:The root of D:(cmd would use D:'s last directory)UNC cd \\server\share\dirThe network path (this is how you enter a UNC location by hand) -
One target, no wildcards.
del *.tmplooks for a file literally named*.tmp. To delete several items, select them with Ins or+(select by wildcard), then press F8. -
del/rmdirnever delete on their own. They only open the same confirmation dialog as F8, and nothing is removed until you accept it. Deletes are permanent (there's no recycle bin)..and..are always rejected as targets, so the dialog can never offer to delete the panel's own directory or its parent. -
A verb needs an argument. A bare
cd(no "print current directory" form) is rejected as unrecognized.
Pressing Enter always clears the line. A rejected command shows its reason in the active panel's bottom-border status line, where it stays until the panel next lists a directory successfully:
| Message | Cause |
|---|---|
'dir' is not a recognized command |
Anything other than cd / del / rmdir with an argument |
C:\Projects\nosuch not found |
The target doesn't exist |
C:\Projects\readme.txt is not a directory |
cd or rmdir on a file |
C:\Projects\docs is a directory |
del on a directory |
rmdir: invalid target .. |
. or .. given to del / rmdir |
Each cd that succeeds is saved to the command history in history.json,
which keeps the most recent 200 entries. Running a command that's already
saved moves it to the newest position. Rejected lines and del/rmdir
aren't saved.
Up/Down only recall history while the line has text (on an empty line they move the panel cursor). To browse history from an empty line, type a space and press ↑:
- Each ↑ steps to an older entry, and each ↓ steps to a newer one.
- A recalled entry replaces the whole line. History isn't filtered by what you've typed.
- Pressing ↓ past the newest entry stops recalling and leaves the text as it is.
- Press Enter to run the recalled entry, or Backspace to edit it.
To fill in a command from the panel instead of typing names:
- Ctrl+Enter adds the cursor entry's file name to the end of the line.
- Ctrl+] adds the cursor entry's full path.
If the line doesn't already end in a space, one is added first. For example, to delete the file under the cursor in the other panel's directory:
- Type
del. - Press Tab, then Ctrl+] to paste the file's full path.
- Press Enter.
Ctrl+] works in every terminal. Ctrl+Enter works on Windows, and elsewhere
only when the terminal supports the kitty keyboard protocol. You can rebind
both with key.paste_name and key.paste_path in
config.toml.
The command line can't run programs, so dir, notepad notes.txt, and
git status are all rejected. There are two ways to launch something:
-
The file-action menu. Press Enter (or right-click) on an executable (any
PATHEXTextension, or a.lnk) and choose Run. Enter never starts an executable directly. -
The F2 user menu. Add an entry to
usermenu.toml:[[entry]] label = "git status" command = "git status"
The command string goes to the configured
shellexactly as written, with no placeholder substitution.
Either way, FileCommand suspends its screen and runs the command in the
active panel's directory using the shell from config.toml (cmd.exe /C
by default; PowerShell adds about 200 ms or more of startup per command).
When the command finishes, press a key to return. Afterwards, Ctrl+O
hides the panels so you can read the output in your terminal's scrollback,
and any key brings the panels back. FileCommand doesn't keep its own copy of
command output; only your terminal's scrollback does.
Navigation & panels
| Key | Action |
|---|---|
| ↑ / ↓ / PgUp / PgDn / Home / End | Move cursor (page size follows panel height) |
| Tab | Switch active panel |
| Enter | Directory: enter it. ..: go to parent. File: opens the file-action menu (Run/View/Edit/Send to clipboard/Copy/Rename/Move/Delete). Command line non-empty: dispatches the built-in verbs — cd navigates, del/rmdir open delete-confirmation, anything else is rejected. Tree mode: navigate/expand. |
| Backspace (empty command line) | Parent directory |
| Ctrl+PgUp | Parent directory |
| Ins | Toggle selection at cursor, cursor advances |
| + / - / * | Select by wildcard / deselect by wildcard / invert selection |
| Alt+letter | Start type-ahead jump to the first matching entry |
| Ctrl+P | Toggle the inline quick filter (narrows the panel to a substring match as you type) |
| Ctrl+J | Fuzzy directory-jump dialog (frecency-ranked, persists across sessions) |
| Alt+F7 | Find file |
| Ctrl+R | Re-read (refresh) the active panel |
| Ctrl+L | Toggle Info display mode on the active panel |
| Ctrl+O | Show terminal scrollback (leaves the alternate screen; any key returns) |
| Ctrl+T / Ctrl+W | New tab / close tab on the active panel |
| Alt+1..9 | Switch to tab n on the active panel |
| Ctrl+←/→ | Shrink / grow the left panel (adjust the split) |
| Ctrl+= | Reset the split to 50/50 |
| Alt+F1 / Alt+F2 | Drive select for the left / right panel |
| Ctrl+F3..F6 | Sort active panel by Name / Extension / Time / Size |
| Ctrl+F7 | Unsorted (raw enumeration order) |
File operations
| Key | Action |
|---|---|
| F3 | View file under cursor |
| F4 | Edit file under cursor (external editor if configured, else the built-in editor; large files open in the viewer instead) |
| F5 | Copy |
| F6 | Rename/Move |
| F7 | Make directory |
| F8 | Delete (confirmation; a second confirmation for non-empty directories) |
| Ctrl+C or Ctrl+Ins | Copy the cursor entry (or selection) to the Windows clipboard as file objects, pasteable into Explorer |
| Ctrl+Shift+Ins | Copy the cursor entry's (or selection's) absolute path(s) to the clipboard as text |
| Ctrl+Enter | Paste the cursor entry's file name onto the command line |
| Ctrl+] | Paste the cursor entry's full path onto the command line |
General
| Key | Action |
|---|---|
| F1 | Help |
| F2 | User menu (from usermenu.toml) |
| F9 | Open the pull-down menu bar |
| F10 or Esc | Request quit (Y/N confirmation) |
| Esc (in a dialog/menu/overlay) | Cancel or close it |
| Up / Down (command line non-empty) | Recall previous/next command-line history |
All of the bindings marked overridable in Configuration
(paste_name, paste_path, quick_filter, fuzzy_jump, split_left,
split_right, split_reset, clipboard_files, clipboard_paths) can be
remapped in config.toml; the rest of the table above is fixed.
Enabled by default; disable with [mouse] enabled = false or --nomouse.
Mouse is only honored in contexts a key press would also reach (panels, the
key bar, menu titles/items, and dialog buttons) — it does nothing in the
viewer/editor beyond wheel scrolling, and nothing at all while an
unsupported overlay (drive select, fuzzy jump, find file, user menu, theme
picker, help, startup warning) is open.
| Gesture | Action |
|---|---|
| Left click on an entry | Focus that panel and move the cursor to the entry |
| Ctrl+left click on an entry | Toggle that entry's selection in place |
| Double left click on an entry | Same as Enter |
| Left click on empty panel area / title | Focus that panel |
| Right click on an entry | Open the file-action menu for it |
| Left-drag from an entry onto a target | Propose a Copy |
| Right-drag, or Shift+drag, from an entry | Propose a Move |
| Esc during a drag | Cancel the drag |
| Scroll wheel over a panel | Scroll 3 lines |
| Click a key-bar slot | Activate that F-key |
| Click a menu-bar title / pull-down item | Open / activate it; clicking outside an open pull-down closes it |
| Click a dialog button | Activate it |
Five menus, navigated with ←/→ (between menus), ↑/↓ (within one), Enter, Esc, or a hotkey letter:
- Left / Right (mirror each other, acting on their own panel): display mode (Brief/Full/Tree/Quick view/Info), sort mode, re-read, drive select, new/close tab.
- Files: View, Edit, Copy, Rename/Move, Make directory, Delete, copy to clipboard (files/paths/names), Select/Deselect/Invert group, Quit.
- Commands: Find file, Fuzzy jump, Panels on/off. (History, Swap panels, Compare directories, and Menu file edit are listed but not yet implemented — greyed out.)
- Options: Themes (opens the live theme picker). (Configuration, Editor selection, and Save setup are listed but not yet implemented.)
| Mode | Description |
|---|---|
| Full (default) | Name | Size | Date | Time columns |
| Brief | Three columns of names only |
| Info (Ctrl+L) | Drive/system/directory info instead of a listing |
| Tree | Lazily-expanded directory tree of the current drive; moving the cursor updates the opposite panel's listing |
| Quick view | Live preview (text head) of the file under the opposite panel's cursor |
Copy (F5), Move/Rename (F6), Mkdir (F7), and Delete (F8) run as cancellable background jobs with a progress dialog (file/byte counts, a cancel button). Overwrite conflicts prompt Overwrite/Skip/Rename/Overwrite All/Skip All; errors (permission denied, path too long, disk full, sharing violation) pause the job for Retry/Skip/Skip All/Abort, and skipped files are listed in a summary at the end. Same-volume moves are instant renames; cross-volume moves copy then delete only after the copy verifies. There is no recycle bin — deletes are permanent, and the confirmation dialog says so.
- Viewer — read-only, opens instantly at any file size (streams/memory-maps rather than indexing). F2 toggles wrap, F4 toggles text/hex mode, F7 searches, F10/Esc closes.
- Editor — F4 opens your configured external editor if
editor =is set inconfig.toml; otherwise the built-in editor (files under 10 MB; larger files open in the viewer instead). F2 saves, F3 marks a line-selection anchor, F4 opens search-and-replace, F7 searches, Ctrl+X/C/V cut/copy/paste, Ctrl+Z undoes, F10 quits (prompting to save if modified).
Six built-in themes: nc-classic (default, the authentic NC palette),
nc-mono (black/white), terminal-green, purple-lights, yellow-storm,
and inverted. Switch live via Options → Themes (arrow keys preview
each theme before you commit) or the F2 user menu; the choice persists to
config.toml. Pin a theme for a single session with --theme <name>
without touching the saved default.
FileCommand/
├── crates/
│ ├── filecommand-core/ # state, reducer, fs ops, git info — no UI deps
│ └── filecommand-tui/ # ratatui + crossterm binary (the `filecommand` exe)
├── installer/ # WiX (v4/v5) MSI + Burn bootstrapper packaging
├── openspec/ # OpenSpec proposals and per-capability specs
│ └── specs/ # source of truth for current behavior
├── docs/superpowers/specs/ # original full design document
└── Screen/ # screenshots used in this README
cargo test --workspaceCovers filecommand-core unit and property-based tests (file ops against
temp directories, sorting/filtering, selection semantics, config/theme
parsing, git info against fixture repos) and filecommand-tui ratatui
TestBackend snapshot tests (panels, dialogs, viewer/editor, splash, help,
menus) via insta.
This project develops behind an OpenSpec-driven, branch-per-change
workflow: research and proposals happen on Spec, get merged to main,
then each approved proposal is implemented on its own build/<name> branch
off main. Nothing lands on main without an explicit human go-ahead. See
CLAUDE.md for the full rules.
Found a bug or want to request a feature? File it on GitHub: https://github.com/kkaminsk/FileCommand/issues
A good report includes:
- What you did — the keys pressed or command typed, step by step.
- What happened versus what you expected.
- Environment — Windows version, terminal (Windows Terminal, conhost, PowerShell), and the FileCommand version shown on the startup splash.
- Error text — anything shown on the panel's status line or in a job dialog, copied verbatim. The UI is plain text, so pasting the screen works as well as a screenshot.
Dual-licensed under MIT or Apache-2.0, at your option.

