Skip to content

Latest commit

 

History

368 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nsh

Table of contents

nsh is Not a SHell!

A cross-platform (Windows / Linux / macOS) interactive file explorer + shell, rewritten in Python on top of prompt_toolkit. It is CJK-aware throughout and includes FTP/SFTP remote browsing.

Current version: 1.0.0.

Its primary modes are:

  1. Explorer (default) — an mc/lf-style directory pane with type-coloured entries, icons, a live Git status overlay, multi-select, file operations, and a side preview pane. Press : to drop into the shell, / to fuzzy-find.
  2. Command-line — drives your host shell with syntax highlighting, an interactive Tab-completion popup, ANSI-coloured / progress-bar-aware output, and a scrollback you can page through. Press ESC to return to the explorer.
  3. Fuzzy search — an fzf-style file picker (/ from the explorer, or nsh search from the command line).
  4. Network — an FTP/SFTP remote browser with recursive transfers and file operations, opened from F10 → Network.

Everything runs in tabs: each tab is its own explorer + shell pair, so you can keep several working directories — each with its own command-line session — open at once and switch between them with F7/F8.

Git/diff and log views, persistent Notes, the System process viewer, recursive text Find, and the searchable Preferences editor are integrated into the same keyboard- and mouse-driven interface.

Install / run

Requires Python 3.7+.

pip install -e .            # installs prompt_toolkit + wcwidth, and an `nsh` command

The -e (editable) install puts an nsh command on your PATH that runs from any directory; because it's editable, later code edits take effect with no re-install. Then, from anywhere:

nsh                         # explorer mode
nsh shell                   # start in command-line mode
nsh search [WORD]           # fuzzy-pick a file; the choice is printed to stdout
nsh -h | -v

If the nsh command isn't found after installing, your Python user-scripts directory isn't on PATH. Print it and add it to PATH:

python -c "import sysconfig,os; print(sysconfig.get_path('scripts', os.name=='nt' and 'nt_user' or 'posix_user'))"
  • Linux / macOS — this is usually ~/.local/bin; add it to PATH in your shell rc.
  • Windows — with the Microsoft Store Python it's a …\LocalCache\local-packages\Python3x\Scripts folder that isn't on PATH by default; add it under Settings → Edit environment variables → Path, then open a new terminal.

As a fallback that needs no PATH change, run it as a module: python -m nsh.

Keys

Tabs

Every explorer is paired with a shell in a tab — its own directory, selection, preview and command-line scrollback — so several places stay open at once and the process working directory follows the active tab.

Key Action
Ctrl+T new tab (a fresh explorer at the current directory)
Ctrl+W close the current tab
F2 (in shell) set or clear the current tab's custom name
F7 / F8, Alt+← / Alt+→ previous / next tab

These work from both the explorer and the command line. The tab bar is also clickable: click a tab to switch to it, double-click it to close it, and click the + button at its right end to open a new tab. The two-pane view (2) is per-tab, so one tab can be split while another shows a single pane. Unless renamed with F2, a tab is named after its current directory and the name follows directory changes (and the active pane in a two-pane tab). Clearing a custom name in the rename dialog restores this automatic naming. Shift+H / Shift+L move focus left / right across the on-screen columns — the two panes, or (in single-pane view) the list and its preview — or click a pane to focus it.

Global shortcuts

Key Action
F10 open the main menu: bookmarks, find, network/disconnect, notes, system, preferences, and about
Ctrl+F choose file-name fuzzy search or recursive text search (Explorer/Git)
Ctrl+N open Notes from Explorer, Git, shell, or System
Ctrl+G toggle Git mode
Ctrl+Q quit nsh
Ctrl+C interrupt the active local/SSH command; with no running shell command, clear its input

Explorer mode

The action keys (everything below the navigation block) are remappable in nshrc — see Configuration.

Key Action
g / Home, G / End first / last row
/, k/j move cursor
open the file / enter the directory
l, expand/collapse a directory inline; on a file, focus the preview
, h, collapse the directory, else go to the parent
Shift+H / Shift+L move focus left / right; only Explorer single-pane may open the cursor directory as a new right pane, while existing two-pane and SSH/Network views only move focus
Space select / deselect the entry (multi-select)
* select entries by substring or glob pattern
Tab open the action menu (copy, rename, delete, git…) — drops from the cursor row, beside the filename
y / x / p copy / cut / paste — the picked rows briefly flash; paste lands in the directory at the cursor. The clipboard is shared across tabs, so you can copy in one tab and paste in another
F2 / i rename (inline)
m / N new folder / new file — created in the directory at the cursor
d move to the platform Trash / Recycle Bin (asks to confirm)
D permanently delete (asks to confirm)
Tabchmod… change permissions using 755, rwxr-xr-x, u+x, or interactive read/write/execute checks
b bookmarks — add/remove this directory, or jump to a saved one
~ jump to the home directory
- open the recently visited directories menu
s sort by name, size, date, or type, ascending or descending
2 toggle two independent Explorer panes
z zoom the focused pane
/ fuzzy-find a file
Ctrl+G git mode — the repository's changed files (see below)
: switch to command-line mode
P toggle the preview pane
. toggle hidden files
r refresh (the listing also auto-refreshes when the directory changes)
? show this key list
ESC clear the selection
q quit

Read-only files inside deleted directories are made writable automatically. Any file-operation error opens an error dialog with the affected paths and details.

Local symbolic links use the link icon and display their recorded target as name -> target, matching the SFTP file view. Directory links end in the local platform's path separator on both sides of the arrow, such as name\ -> target\ on Windows and name/ -> target/ on POSIX.

Paste, new file and new folder follow the cursor: a directory under the cursor is the target (the item lands inside it and it expands to show the result), while a file targets its containing directory — so in the tree view you act exactly where you're pointing. Paste is stricter: it only drops inside a directory you've already expanded; on a collapsed one it pastes into the container beside it, not into a folder you can't see into.

Git actions (stage / unstage, commit, diff, and a Branches submenu that lists branches to check out plus a + New Branch entry) live in the Tab action menu when the directory is a repository. An untracked file can be staged — including the files inside a brand-new directory, which carry the untracked marker and so can be added too.

Repository status uses one git status --porcelain=v2 --branch --show-stash process per refresh, providing branch, upstream, ahead/behind, stash, and file state together. Repository roots and operation markers are read from .git metadata without starting additional Git processes. The background watcher also tracks lightweight Git metadata, so commits, staging, branch movement, and similar state-only changes refresh even when no worktree file changes.

Commit, Revert, and Log make their scope explicit in the action menu. The . entry targets the current directory, while a second entry targets the selected file or files (for example, Git: Commit ., Git: Commit README.md, or Git: Commit 3 files). Directory Git markers summarize tracked changes below a collapsed directory; the aggregate marker disappears when the directory is expanded. Untracked files do not mark their parent directory, and the synthetic .. navigation row never shows a Git marker.

Outside a Git repository, visible child directories—including directories revealed through inline expansion—that are themselves Git repository roots get a blank colour marker: green when there are no tracked changes and red when it has staged, modified, or conflicted tracked files. Untracked files alone do not turn the repository marker red. The colour marker remains on an expanded repository while the usual M/S/! markers also appear on its changed files and collapsed subdirectories.

When the Explorer cursor is on a tracked modified, staged, or conflicted file, the preview shows that file's Git diff instead of its complete contents. Clean and untracked files keep the normal file preview. Image files render inline on iTerm2 using its native image protocol, and on Windows Terminal 1.22 or newer using Sixel when Pillow is installed (pip install "nsh[images]"). On unsupported terminals (or without Pillow for Sixel), the preview keeps showing image metadata. With focus in a Git diff preview, Up/Down jump between contiguous -/+ change blocks and u reverts only the current block after confirmation; s stages or unstages only that block, and j/k move between change blocks just like the arrow keys. The current block has a soft gray background, configurable as preview-hunk-selected under Preferences colors. This background-only style keeps the diff's original addition, deletion, and metadata text colors. Files with both kinds of changes show separate Unstaged changes and Staged changes sections; change navigation and s continue across both. Selected staged blocks use a separate soft-green background configured as preview-hunk-staged-selected, while unstaged blocks retain the gray background. For an unmerged file, the same navigation selects one <<<<<<< / >>>>>>> conflict block. Press s to accept ours, theirs, or both from a menu. Resolving the last block stages the file; u restores the most recently resolved block and its unmerged Git index state.

Picking a branch opens a per-branch menu (Checkout, Browse, Delete). Browse pops a small centered dialog listing that branch's files without checking it out: / (or j/k) move, / l / step into a directory and / h / back out, and y copies the highlighted file — or a whole directory — out of the branch into the current explorer directory (never clobbering an existing name). Esc / q closes it.

Git mode

Ctrl+G opens a flat, git status-style list of the repository's changed and untracked files — a change in a subdirectory shows as its full path (not a tree). / move, Space multi-selects, and the preview pane shows the file's diff (untracked files show their new content). Tab opens an action menu (stage / unstage — applied to the whole selection — commit, edit, branches). There is no directory hierarchy, so /l steps into the diff preview to scroll it (h or Esc returns to the list) while the other left/right keys are inert; Ctrl+G or ESC returns to the explorer, and jumping elsewhere (e.g. via a bookmark) leaves git mode automatically. The git log (from the Tab action menu) works the same way — /l focuses the commit-detail/diff preview and h returns to the log list.

Git mode and the git log are per-tab — each tab keeps its own changed-file list and history (cursor, selection and search) — so F7/F8 (or Alt+←/Alt+→) switch tabs from here too, swapping the view along with the directory it belongs to, and Ctrl+T/Ctrl+W open and close tabs without leaving the mode. Which mode a tab is in is itself per-tab: leaving git/log mode in one tab doesn't pull the others out of it, and switching tabs shows each one in the mode you last left it in (a new tab opens in your current mode, so Ctrl+T keeps your workflow).

Command-line mode

Key Action
typing live syntax highlighting
paste a single line is inserted; multiple lines run in order through the command queue
Tab completion popup; / or j/k navigate, Tab picks (no space), Space picks and adds a space
/ command history (when no popup is open)
run the command
PgUp/PgDn, Alt+↑/Alt+↓, wheel, Ctrl+End scroll the output (the prompt hides while scrolled up)
Ctrl+T / Ctrl+W open / close a tab
F2 rename the current tab; an empty name restores its directory-based name
Alt+← / Alt+→ (or F7 / F8) previous / next tab
ESC clear a non-empty command line; when already empty, switch back to Explorer

Each tab pairs this shell session with its own explorer (see Tabs), so switching tabs swaps the whole working context. Entering a command while the current one is still running queues it immediately in that tab; several commands can wait and run in order. Queued commands are listed in grey above the prompt, with a ⋯ N queued count in the status bar. A tab bar marks each session's state — orange while a command is still running, red once one finishes with a non-zero exit (cleared when the next command runs).

The current completion row uses terminal reverse video while retaining its file/command colour; it is not a separate Preferences colour setting.

Ctrl+Z removes the last waiting command without affecting the command already running. This shortcut is configurable as queue_remove_last in Preferences.

Built-ins handled internally: cd, clear/cls, exit/quit. The output pane grows with its content and goes full-screen once it fills up. Long lines wrap. PowerShell completions keep a leading ~ visible in the editable command; home paths are expanded only before execution while retaining wildcards (for example, mv ~/Desktop/*.jpg .). The prompt shows each command's run time tinted by its exit status (and dims itself while a command is still running), and interactive commands that need a real terminal — editors/pagers, plus network git (push/pull/fetch/clone) and sudo that may prompt for credentials — run with the UI briefly suspended. nsh echoes the prompt + command above their output and, when they finish, waits for a keypress (press any key to continue …) so it stays visible. Prefix any command with ! to force it onto the real terminal this way — an escape hatch for a TUI nsh doesn't recognise on its own (e.g. !htop).

Commands listed in the Preferences variable external_commands take the same real-terminal path without requiring !. Separate names with spaces or commas, for example mc, ranger, lazygit. Forced and configured external commands record their elapsed time and exit status just like commands captured in nsh's scrollback.

Fuzzy search mode

Type to filter, / to move, to select, ESC to cancel. Launched with nsh search [WORD] (prints the selection to stdout, e.g. cd "$(nsh search)") or with / from the explorer. Build outputs (build/, dist/) are indexed too, so a built executable is findable; the skipped directories are configurable via search_exclude (see Configuration).

Search does not wait for a full tree index before becoming useful: matches from the current directory appear first, then indexed results are added as background indexing progresses. While that index is still running, a query containing / or \ follows its fuzzy-matched directory components and lists that specific subdirectory immediately. Remote search follows the same visible-results-first model.

Notes mode

Open Notes with Ctrl+N or F10 → Notes. Notes are persistent, multi-line scratch entries. Type in the upper editor and press Ctrl+S to add or update a note; Down moves into the list. In the list, arrows or j/k navigate, Enter edits, / searches, y copies the full note to the system clipboard, d/x deletes, and u restores the last deletion. Esc clears an active search or edit first, then returns to the originating view.

System mode

Open F10 → System for a live process list. Arrows or j/k, page keys, and g/G navigate; c, m, and n sort by CPU, memory, and name; v toggles the detailed command line; / filters; r refreshes; and x, Delete, or K asks to terminate the selected process. Esc clears a search first, then returns to the originating view.

Network mode (FTP / SSH-SFTP)

Network mode places the active local explorer pane on the left and an FTP or SSH-SFTP directory tree on the right. Open F10 → Network from the local pane you want to use as the transfer endpoint, then choose a connection type. Both directories remain visible while files are transferred between them. Searching with / in the local pane returns to the same Network layout and keeps the remote connection visible; remote search likewise restores remote focus. Notes, grep/shell, System, Preferences, Git/Log, and modal dialogs also return to the originating Network pane instead of replacing the SSH side with preview.

The connection belongs to the app, not to one tab, so the layout stays exactly tWo panes everywhere. A tab opened (Ctrl+T) or switched to while connected ShOWS itS own local directory on the left and the same remote tree on the right, with the cursor starting on the local side; the preview and the two-pane explorer split step aside for the duration. Transfers always use the local pane you can see. Disconnecting returns every tab to its ordinary explorer layout, preview included.

Connection targets use [user@]host[:port][/initial/path]:

alice@example.com:22/home/alice       # SFTP
files@example.com:21/incoming         # FTP
internal-server/projects              # SSH alias + initial path

SFTP defaults to port 22 and the current operating-system user; FTP defaults to port 21 and anonymous login. The password dialog is masked and cleared as soon as it closes. Leave the SFTP password blank to use IdentityFile entries, your SSH agent, or Paramiko's normal private-key discovery. Leave an anonymous FTP password blank to use anonymous@.

After a successful SFTP connection, its target (never its password) is saved in the UI state and prefilled the next time the SFTP connection dialog opens.

SSH config and jump hosts

SFTP resolves aliases through ~/.ssh/config, including HostName, User, Port, IdentityFile, ProxyJump, and ProxyCommand. For example:

Host bastion
    HostName bastion.example.com
    User jumpuser
    IdentityFile ~/.ssh/bastion_ed25519

Host internal
    HostName 10.20.0.15
    User deploy
    IdentityFile ~/.ssh/internal_ed25519
    ProxyJump bastion

Choose SFTP and enter internal/var/www as the target. At the next prompt, leave Jump host empty: nsh reads ProxyJump bastion from the config and opens the final SFTP session through an SSH direct-tcpip channel.

A jump host can also be entered explicitly as jumpuser@bastion.example.com:2222. Comma-separated chains such as edge@edge-host,core@core-host are supported. An explicit value overrides the destination's ProxyJump setting. Each hop resolves its own SSH config and is closed in reverse order when the connection ends or fails.

When no jump host or ProxyJump is active, nsh also supports ProxyCommand. A standard ssh -W %h:%p configured-alias form (including combined flags such as ssh -AW) uses nsh's native direct-tcpip jump path, so the alias's User, Port and IdentityFile are applied just like an explicit jump host. Other proxy commands open as the SSH transport socket. Standard tokens such as %h, %p, and %r are expanded by the SSH config parser, and any proxy process is closed when the connection ends or fails. You may enter either the Host alias or its literal HostName in nsh; when exactly one matching alias defines proxy routing, nsh applies that alias's ProxyJump or ProxyCommand automatically. When this routing is detected after entering the SFTP target, nsh skips the redundant Jump host dialog and proceeds directly to authentication.

Every SSH host key is checked independently against the user's system known_hosts. On first connection nsh shows the key type and SHA256 fingerprint for approval; verify it with the server owner. An approved key is saved to ~/.ssh/known_hosts and the connection is retried. A key that conflicts with an existing entry is still rejected without an approval prompt. A password entered in nsh is available as an authentication fallback for the destination and every jump host, while per-host keys and the SSH agent remain preferred alternatives. If authentication is rejected, nsh preserves the target and jump route and asks only for the password again.

Browsing and file operations

Key Action
/, j/k move cursor
g/Home, G/End first / last row
~ open the remote login home directory
s sort by name, size, date, or type in either direction
/ fuzzy-find files and directories below the remote directory
: open a command shell over the current SSH connection
enter a directory; download a file
l, expand / fold a remote directory inline
, h, fold, move to the tree parent, or open the parent directory
Space select / deselect
Shift+H / Shift+L focus the local / remote pane
c local focus: upload; remote focus: download
n create remote directory
i rename remote item
D permanently delete selected remote items
Tab remote actions menu
r refresh
Esc clear the focused pane's selection
q quit nsh

With the remote pane focused, c downloads into the displayed local pane's current directory. With the local pane focused, c uploads its marked selection—or its cursor item when nothing is marked—into the displayed remote directory. Files and whole directory trees are supported in both directions. Enter on an SFTP file opens a bounded preview in the remote pane without creating a local file; Esc returns to the listing and c explicitly downloads the file. Binary files are downloaded to an instance-private temporary directory behind a blocking progress dialog with a Cancel button, then opened with the OS default application; those temporary files are removed on disconnect or exit. Enter on a directory opens it. FTP files retain the direct-download behavior. A transfer refreshes only its destination pane.

SFTP uploads and downloads run as visible SSH-shell jobs. Each selected item is queued separately, so the shell shows the source and destination, elapsed time, live progress bar, transferred/total size, transfer speed, completion or failure details, and subsequent transfers waiting above the prompt. Ctrl+C cancels the active transfer; queued transfers then continue in order. Ctrl+Z removes the last queued command or transfer without cancelling the active one. The SSH shell initially shares the screen with the local/remote panes and grows with its output, maximizing only when the normal shell height limit is reached. FTP transfers retain the direct network-pane workflow because FTP has no SSH command shell.

The remote pane uses the same tree presentation as the local file pane: file sizes are right-aligned, directories use / carets, expanded children are indented, and the cursor highlights the complete row with the explorer colour scheme. A .. row is shown outside the remote root. Directory contents are fetched only when first expanded.

SFTP symbolic links use the link icon and show name -> target. Directory links use name/ -> target/. Links to directories can be opened and expanded like directories; links to files behave like files, and broken links are marked with the conflict color. Recursive search does not follow directory links, and deleting a directory link removes only the link rather than the target tree.

On an SFTP connection, : opens a remote command shell that reuses the active authenticated SSH session. Commands run in the directory displayed by the remote file pane. cd updates that pane's directory, clear clears remote shell output, and exit/quit or Esc returns to the remote files. Commands entered while another command is running are shown above the prompt and executed in queue order. Running and completed commands show elapsed-time badges using the same colours and alignment as the local shell. Commands are executed one at a time without an interactive PTY, so full-screen programs such as vim and top are not supported in this view.

Transfers and recursive delete operations run in worker threads so the TUI can continue repainting. Existing names are never overwritten: uploads and downloads use name (2), name (3), and so on. Disconnect is blocked while an operation is active, but a background remote search index is cancelled so it does not prevent disconnecting. Connection/transfer errors remain visible in the status bar.

Disconnect deliberately has no single-key shortcut. While connected, the F10 menu replaces Network with Network: Disconnect; choose it, or use Disconnect from the Tab remote-actions menu, and approve the confirmation prompt. The 2 key is disabled while this fixed local/remote layout is open, so it cannot alter the hidden local explorer split.

Current limitations: transfers cannot yet be resumed, there is no remote-to- remote copy/move or chmod action, credentials are not saved, and only plain FTP (not FTPS) is implemented. Plain FTP sends credentials and data without encryption and should only be used with trusted legacy servers; prefer SFTP for normal use.

Configuration

Open F10 → Preferences to enter the full-screen settings editor. Its single list contains variables, colors, and shortcuts; type in the search field to filter by category, setting name, or current value, then press Enter to edit the selected row. Changes are validated, saved, and applied immediately. Leave a color blank to restore its built-in default; leave a shortcut blank to unbind that action. Rows that differ from their defaults have a leading *; editing one also offers a Reset Default button. Press Ctrl+O to open nshrc in an external editor for advanced manual editing.

The settings are stored in ~/.config/nsh/nshrc ($XDG_CONFIG_HOME is honoured). On first run nsh seeds a commented template. It is a simple INI file:

[general]
# editor for the "Edit" action; unset -> $EDITOR/$VISUAL, then notepad/vi
editor = code -w
two_pane = false                     # start with two explorer panes side by side
restore_tabs = true                  # restore tabs, paths and command history
scrollback_lines = 2000              # retained lines per local/SSH shell tab
search_exclude = .git node_modules   # directories fuzzy search skips
external_commands = mc, lazygit      # run these on the real terminal without !

[colors]
# <style-class> = <prompt_toolkit style>
explorer.dir      = #5fafff bold
explorer.selected = #ffff5f bold
shell.command     = #5fafff bold

[keys]
# <action> = <key>   (a char, or: space, tab, escape, f5, c-r, s-tab, …)
copy   = y
trash  = d
delete = D
menu   = tab
quit   = q

[general] sets the Edit editor (Tab menu, text files only), whether to start in two_pane view, whether restore_tabs restores the previous tab set, pane paths, recent per-tab command history and unfinished command input, the per-shell scrollback_lines limit (1–100000), search_exclude (the directories fuzzy search skips), and external_commands (commands that always receive a real terminal). [colors] overrides any UI style class; [keys] remaps the explorer action keys. Invalid entries are ignored, never fatal.

Bookmarks (the b key) are saved one path per line in ~/.config/nsh/bookmarks.

Architecture

nsh/
  app.py              application modes, layout, central key dispatch, cwd/git state
  network/
    backend.py         FTP/SFTP connection and remote filesystem operations
    view.py            remote browser, transfers and action bindings
    shell.py           command shell over the active SSH connection
  preferences/
    view.py            searchable variables/colors/shortcuts editor
  notes/
    view.py            persistent multi-line notes editor/list/search
  system/
    view.py            sortable, searchable process viewer and terminate action
  config.py           styles, icons, key map, nshrc loading
  util/
    width.py          wcwidth-based truncate/pad (CJK-correct columns)
    paths.py          pathlib helpers, normalised compare keys
    aio.py            run_in_thread (asyncio.to_thread backport for 3.7/3.8)
    widgets.py        mouse-wheel-aware control
    menu.py           reusable popup action menu
  explorer/
    model.py          os.scandir directory listing
    git.py            async git status / branch / stage / commit / diff
    gitview.py        git mode: flat changed-file list (Ctrl+G)
    logview.py        scoped directory/file history and commit actions
    fileops.py        copy / move / trash / delete / rename / mkdir (threaded)
    preview.py        text/dir/hex/diff preview plus Sixel/iTerm2 inline images
    view.py           file-list rendering, navigation, multi-select, action menu
  search/
    fuzzy.py          fzf-style scorer + directory indexer
    view.py           the fuzzy picker
  shell/
    runner.py         host-shell wrap via asyncio subprocesses (per session)
    tabs.py           tabs — each bundles an explorer pair + a shell session
    completer.py      interactive path + command Tab-completion
    lexer.py          command-line syntax highlighting
    view.py           one session: scrollback (ANSI / CR / BS-aware, wrapped) + prompt

Design notes

  • CJK widths. Every column in the file list is padded/truncated with util/width.py, which measures rendered cell width via wcwidth instead of len(), so Korean/Chinese/Japanese names never break the layout. Git output is read with core.quotepath=false to keep unicode filenames intact.
  • Non-blocking everything. Directory listing, Git status, file copies, the preview, and the search index all run off the event loop (an asyncio task or a worker thread) and invalidate() when done; the UI never waits on them. Periodic directory scans skip overlaps, and stale results (for a directory you already left) are dropped. Concurrent identical Git status requests share one process, which is terminated when its final waiter is cancelled.
  • Host shell. Commands run through your platform's default shell ($SHELL -c … on Unix; cmd /c … or powershell -Command … on Windows), streamed in chunks so a \r-only progress bar updates in place. ANSI colour codes are interpreted (and colour is forced on via the child environment, since stdout isn't a TTY); for the same reason PYTHONUNBUFFERED=1 is set so a Python script's prints stream live instead of block-buffering until it exits. Interactive programs (editors, pagers, top…) — and commands that may prompt for credentials, like network git and sudo — are detected and run with the full-screen UI temporarily suspended so they get a real terminal.
  • Cross-platform paths. All path handling uses pathlib, so Windows \ and POSIX / are handled uniformly.

Releases

Packages

Contributors

Languages