How Superfile's Prompt Command Line Mode Executes Shell Commands

Superfile's prompt command line mode captures raw input from the : prompt and executes it through the system shell using Go's exec package, with platform-specific handling for Linux/macOS (/bin/sh) and Windows (powershell.exe), while respecting the focused panel's working directory and configurable timeouts.

Superfile, an open-source terminal file manager written in Go by yorukot/superfile, provides a shell prompt mode that allows users to run arbitrary commands without exiting the TUI. When you type a command after pressing : in superfile, the application does not implement its own shell interpreter; instead, it passes the raw command string directly to the host operating system's shell through a carefully orchestrated five-stage pipeline.

The Execution Pipeline

Superfile's shell command execution follows a defined pipeline that moves from user input capture to result display. Each stage is implemented in specific source files within the repository.

Step 1: Prompt Parsing in Shell Mode

When a user opens the prompt with the : key and enters text, the input is processed by src/internal/ui/prompt/utils.go (lines 15‑18). If the prompt is operating in shell mode (the default), the function getPromptAction returns a common.ShellCommandAction struct containing the raw command string.

// src/internal/ui/prompt/utils.go
return common.ShellCommandAction{
    Command: value, // the raw command string typed by user
}, nil

This action encapsulates the user's intent to run a shell command, distinguishing it from other prompt modes like quick navigation or search.

Step 2: Model Action Dispatch

The main Bubble Tea model in src/internal/model.go (lines 8‑11) receives the ShellCommandAction through its action processing loop. When the model encounters this action type, it immediately delegates to applyShellCommandAction.

// src/internal/model.go
func (m *model) logAndExecuteAction(action common.ModelAction) (string, tea.Cmd, error) {
    switch action := action.(type) {
    case common.ShellCommandAction:
        m.applyShellCommandAction(action.Command)
        return "", nil, nil
    // other action types...
    }
}

This dispatch mechanism keeps the shell execution logic isolated from the main UI update loop.

Step 3: Shell-Specific Preparation

The applyShellCommandAction method retrieves the current working directory from the focused file panel and calls utils.ExecuteCommandInShell. This utility function, located in src/pkg/utils/shell_utils.go (lines 13‑24), determines the appropriate shell binary based on the operating system: /bin/sh for Unix-like systems and powershell.exe for Windows.

// src/pkg/utils/shell_utils.go
func ExecuteCommandInShell(timeLimit time.Duration, cmdDir, shellCommand string) (int, string, error) {
    baseCmd := "/bin/sh"
    args := []string{"-c", shellCommand}
    if runtime.GOOS == OsWindows {
        baseCmd = "powershell.exe"
        args[0] = "-Command"
    }
    return ExecuteCommand(timeLimit, cmdDir, baseCmd, args...)
}

This abstraction ensures cross-platform compatibility without requiring the caller to handle OS detection.

Step 4: Process Creation and Execution

The ExecuteCommand function in src/pkg/utils/shell_utils.go (lines 27‑34) creates a exec.CommandContext with a configurable timeout, sets the working directory to the focused panel's location, and detaches the process from the terminal to prevent interference with the TUI's raw mode.

// src/pkg/utils/shell_utils.go
func ExecuteCommand(timeLimit time.Duration, cmdDir, baseCmd string, args ...string) (int, string, error) {
    ctx, cancel := context.WithTimeout(context.Background(), timeLimit)
    defer cancel()
    cmd := exec.CommandContext(ctx, baseCmd, args...)
    cmd.Dir = cmdDir
    DetachFromTerminal(cmd)  // Prevents terminal mode conflicts
    outputBytes, err := cmd.CombinedOutput()
    // Returns exit code, combined stdout/stderr, and error (if timed out or failed)
}

The DetachFromTerminal call is critical for maintaining UI stability, as it prevents the subprocess from manipulating the terminal settings that superfile's Bubble Tea framework depends on.

Step 5: Result Handling and Display

After execution completes, applyShellCommandAction in src/internal/model.go (lines 35‑44) forwards the exit code and output to the prompt modal via HandleShellCommandResults. The modal displays the results or error messages within the superfile interface, keeping the user in the TUI context.

// src/internal/model.go
func (m *model) applyShellCommandAction(shellCommand string) {
    focusPanelDir := m.getFocusedFilePanel().Location
    retCode, output, err := utils.ExecuteCommandInShell(
        common.DefaultCommandTimeout,
        focusPanelDir,
        shellCommand,
    )
    m.promptModal.HandleShellCommandResults(retCode, output)
    if err != nil {
        slog.Error("Command execution failed", "retCode", retCode,
            "error", err, "output", output)
    }
}

Key Source Files and Responsibilities

Understanding superfile's shell execution architecture requires familiarity with these specific files in the yorukot/superfile repository:

Platform-Specific Behavior

Superfile automatically adapts its shell invocation based on the runtime operating system. On Linux and macOS, it uses /bin/sh -c to execute commands, while on Windows it switches to powershell.exe -Command. This ensures that shell builtins, environment variables, and path handling behave according to user expectations on each platform, without requiring manual configuration.

The working directory is always set to the location of the currently focused file panel, meaning relative paths in your commands resolve correctly against what you see in the UI. The default timeout (defined as common.DefaultCommandTimeout) prevents hanging commands from freezing the interface indefinitely.

Summary

  • Superfile's prompt command line mode delegates all command execution to the system shell rather than implementing a custom interpreter
  • The execution flow moves through five stages: prompt parsing, model dispatch, shell preparation (platform detection), process creation (with timeout), and result handling
  • Cross-platform support is handled automatically via ExecuteCommandInShell, selecting /bin/sh for Unix systems and powershell.exe for Windows
  • Process isolation via DetachFromTerminal ensures the TUI remains responsive and doesn't inherit terminal mode changes from subprocesses
  • Working directory context is preserved by executing commands in the focused panel's location, with configurable timeouts preventing UI hangs

Frequently Asked Questions

How does superfile handle long-running shell commands?

Superfile uses exec.CommandContext with a configurable timeout (common.DefaultCommandTimeout) to prevent commands from hanging indefinitely. If a command exceeds this limit, the context cancels the process, and superfile returns a timeout error to the prompt modal while logging the failure via slog.Error. You can adjust this timeout in the configuration if you need to run longer operations.

Can I use shell aliases and functions in superfile's prompt mode?

Yes, since superfile executes commands through /bin/sh (or powershell.exe on Windows), any aliases or functions defined in your shell's configuration files will be available only if they are loaded by the shell binary being invoked. However, because superfile uses non-interactive shell invocation (typically /bin/sh -c), your .bashrc or .zshrc may not be sourced automatically. For full alias support, you would need to invoke an interactive shell or source your configuration files within the command.

Why does superfile use /bin/sh instead of the user's default shell on Linux?

Superfile defaults to /bin/sh for portability and consistency across different Linux distributions and Unix systems. While your interactive shell might be zsh or fish, /bin/sh is guaranteed to exist on POSIX-compliant systems and provides a predictable execution environment. This ensures that superfile's prompt command line mode behaves consistently regardless of the user's preferred interactive shell.

Where is the shell command output displayed in superfile?

The output appears in the prompt modal itself through the HandleShellCommandResults method in src/internal/ui/prompt/model.go. After execution completes, superfile displays the combined stdout and stderr output directly in the prompt area, along with the exit code. If the command fails (non-zero exit code), the error is logged internally, but the output is still presented to the user within the modal interface.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →