Skip to content
Back to top
View this page
Edit this page

# Prompt

Xonsh ships with two REPL engines. Historically, they have been referred to in xonsh as “shells” or “prompts”, but they will likely be renamed in the future, as they are in fact REPL (read–eval–print loop) engines that power all user interaction with the terminal. These are:

- prompt-toolkit or ptk (`$SHELL_TYPE` is `prompt_toolkit`) — the recommended, full-featured engine built on prompt_toolkit. It provides syntax highlighting, multi-line editing, completion menus, custom key bindings, and more. It is included when installing the full package (`pip install 'xonsh[full]'`).

- readline (`$SHELL_TYPE` is `readline`) — a minimal fallback used when ptk cannot be started for some reason (for example, when `prompt_toolkit` is not installed).

This page describes features and customization options of the ptk engine. For the underlying library reference, see the prompt_toolkit official docs.

## Customizing the Prompt

Customizing the prompt by modifying `$PROMPT`, `$RIGHT_PROMPT` or `$BOTTOM_TOOLBAR` is probably the most common reason for altering an environment variable.

The `$PROMPT` variable can be a string, or it can be a function (of no arguments) that returns a string. The result can contain keyword arguments, which will be replaced automatically:

    @ $PROMPT = '{user}@{hostname}:{cwd} @ '
    snail@home:~ @ # it works!
    @ $PROMPT = lambda: '{user}@{hostname}:{cwd} @> '
    snail@home:~ @> # so does that!

By default, the following variables are available for use:

- `user`: The username of the current user

- `hostname`: The name of the host computer

- `cwd`: The current working directory. Use `$DYNAMIC_CWD_WIDTH` to set a maximum width and `$DYNAMIC_CWD_ELISION_CHAR` for the elision character.

- `short_cwd`: A shortened form of the current working directory; e.g., `/path/to/xonsh` becomes `/p/t/xonsh`

- `cwd_dir`: The dirname of the current working directory, e.g. `/path/to/`

- `cwd_base`: The basename of the current working directory, e.g. `xonsh`

- `env_name`: The name of active virtual environment, if any.

- `env_prefix`: Prefix characters for active virtual environment (default `"("`).

- `env_postfix`: Postfix characters for active virtual environment (default `") "`).

- `curr_branch`: The name of the current git branch, if any.

- `branch_color`: `{BOLD_GREEN}` if the current git branch is clean, otherwise `{BOLD_RED}`. Yellow if undetermined.

- `branch_bg_color`: Like `{branch_color}`, but sets a background color.

- `prompt_end`: `@#` if the user has root/admin permissions, `@` otherwise.

- `current_job`: The name of the command currently running in the foreground.

- `gitstatus`: Informative git status, like `[main|MERGING|+1…2]`. See `xonsh.prompt.gitstatus` for customization options.

- `localtime`: The current local time, formatted with `time_format`.

- `time_format`: A time format string, defaulting to `"%H:%M:%S"`.

- `last_return_code`: The return code of the last issued command.

- `last_return_code_if_nonzero`: The return code if non-zero, otherwise `None`.

## Colors

Xonsh supports colored output in prompts, `print_color`, and `printx`. Color keywords such as `{GREEN}` or `{BOLD_BLUE}` can be used in prompt strings and with color printing functions.

### Color Names

- COLORNAME: Inserts a color code for the following basic colors, which come in regular (dark) and intense (light) forms:

  - `BLACK` or `INTENSE_BLACK`

  - `RED` or `INTENSE_RED`

  - `GREEN` or `INTENSE_GREEN`

  - `YELLOW` or `INTENSE_YELLOW`

  - `BLUE` or `INTENSE_BLUE`

  - `PURPLE` or `INTENSE_PURPLE`

  - `CYAN` or `INTENSE_CYAN`

  - `WHITE` or `INTENSE_WHITE`

- `DEFAULT`: The color code for the terminal’s default foreground color.

- `#HEX`: A `#` before a len-3 or len-6 hex code will use that hex color, or the nearest approximation that that is supported by the shell and terminal. For example, `#fff` and `#fafad2` are both valid.

- `BACKGROUND_` may be added to the beginning of a color name or hex color to set a background color. For example, `BACKGROUND_INTENSE_RED` and `BACKGROUND_#123456` can both be used.

- `bg#HEX` or `BG#HEX` are shortcuts for setting a background hex color. Thus you can set `bg#0012ab` or the uppercase version.

### Color Modifiers

- `BOLD_` — increases font intensity. E.g. `BOLD_RED`, `BOLD_#112233`.

- `FAINT_` — decreases font intensity. E.g. `FAINT_YELLOW`.

- `ITALIC_` — switches to italic. E.g. `ITALIC_BLUE`.

- `UNDERLINE_` — adds underline. E.g. `UNDERLINE_GREEN`.

- `SLOWBLINK_` — slow blinking text. E.g. `SLOWBLINK_PURPLE`.

- `FASTBLINK_` — fast blinking text. E.g. `FASTBLINK_CYAN`.

- `INVERT_` — swaps foreground and background. E.g. `INVERT_WHITE`.

- `CONCEAL_` — hides text (may not be widely supported). E.g. `CONCEAL_BLACK`.

- `STRIKETHROUGH_` — draws a line through text. E.g. `STRIKETHROUGH_RED`.

Each modifier has an `OFF` variant to disable it: `BOLDOFF_`, `FAINTOFF_`, `ITALICOFF_`, `UNDERLINEOFF_`, `BLINKOFF_`, `INVERTOFF_`, `CONCEALOFF_`, `STRIKETHROUGHOFF_`.

Modifiers can be combined: `BOLD_UNDERLINE_INTENSE_BLACK`.

### Reset Colors

- `RESET`: Resets any previously used styling.

- `RESET_FOREGROUND`: Resets only the foreground (text) color to the terminal’s default, leaving the background color unchanged.

- `RESET_BACKGROUND`: Resets only the background color to the terminal’s default, leaving the foreground color unchanged.

## Additional Prompt Variables

You can make use of additional variables beyond the defaults by adding them to the `PROMPT_FIELDS` environment variable. The values in this dictionary should be strings (which will be inserted into the prompt verbatim), or functions (which will be called each time the prompt is generated, and the results of those calls will be inserted into the prompt). For example:

    snail@home ~ @ $PROMPT_FIELDS['test'] = "hey"
    snail@home ~ @ $PROMPT = "{test} {cwd} @ "
    hey ~ @
    hey ~ @ import random
    hey ~ @ $PROMPT_FIELDS['test'] = lambda: random.randint(1,9)
    3 ~ @
    5 ~ @
    2 ~ @
    8 ~ @

Here is an example — a random emoji before the prompt character:

    from xonsh.completers.emoji import get_emoji_cache
    $PROMPT_FIELDS['random_emoji'] = lambda: @.imp.random.choice(get_emoji_cache())[0]
    $PROMPT = $PROMPT.replace("{prompt_end}", "{random_emoji} {prompt_end}")

    snail@home ~ 🥗 @  # It helps to visually
    snail@home ~ 🍎 @  # identify lines
    snail@home ~ 🧀 @  # in your scrollback history

Environment variables and functions are also available with the `$` prefix. For example:

    snail@home ~ @ $PROMPT = "{$LANG} >"
    en_US.utf8 >

Note that some entries of the `$PROMPT_FIELDS` are not always applicable, for example, `curr_branch` returns `None` if the current directory is not in a repository. The `None` will be interpreted as an empty string.

But let’s consider a problem:

    snail@home ~/xonsh @ $PROMPT = "{cwd_base} [{curr_branch}] @ "
    xonsh [main] @ cd ..
    ~ [] @

We want the branch to be displayed in square brackets, but we also don’t want the brackets (and the extra space) to be displayed when there is no branch. The solution is to add a nested format string (separated with a colon) that will be invoked only if the value is not `None`:

    snail@home ~/xonsh @ $PROMPT = "{cwd_base}{curr_branch: [{}]} @ "
    xonsh [main] @ cd ..
    ~ @

The curly brackets act as a placeholder, because the additional part is an ordinary format string. What we’re doing here is equivalent to this expression:

    " [{}]".format(curr_branch()) if curr_branch() is not None else ""

## Multiline Prompt

When you enter a multi-line statement (`for` loop, `if` block, etc.), xonsh displays a continuation prompt on each subsequent line. The pattern is controlled by `$MULTILINE_PROMPT` (default `" "`).

The value is repeated to fill the width of the main prompt. It can be a plain string, a string with color markup, or a callable:

    prompt @ $MULTILINE_PROMPT = '~*'
    prompt @ for i in range(3):
    ~*~*~*~*     print(i)
    ~*~*~*~*

Both xonsh color keywords (`{RED}`) and ANSI escape codes (`\033[31m`) are supported.

### Callable with `line_number` and `width`

When `$MULTILINE_PROMPT` is a callable, it receives two keyword arguments:

- `line_number` — the line number of the continuation line. The main prompt is line 1, so the first continuation is `line_number=2`.

- `width` — the visible width (in columns) of the main prompt.

This lets you render unique content per line, for example line numbers:

    _ml_colors = ['{CYAN}', '{GREEN}', '{YELLOW}', '{BLUE}', '{PURPLE}', '{RED}']

    def _multiline(line_number, width):
        c = _ml_colors[line_number % len(_ml_colors)]
        return f'{c}{line_number:>{width - 2}}|{{RESET}} '

    $MULTILINE_PROMPT = _multiline

Result (each line is a different color):

    prompt @
          2|    for i in range(5):
          3|        if i > 2:
          4|            print(i)
          5|

Existing callables that accept no arguments continue to work.

## Custom Keybindings

For the list of default key bindings shipped with xonsh, see Keyboard Shortcuts.

The `prompt_toolkit` shell has a registry for handling custom keybindings. You may not like the default keybindings in xonsh, or you may want to add a new key binding.

This section walks you through how to do this using `prompt_toolkit` tools to define keybindings and warns you about potential pitfalls.

All of the code below can be entered into your xonsh RC.

This will let you hook directly into the `prompt_toolkit` keybinding manager. It will not stop you from rendering your prompt completely unusable, so tread lightly.

### Control characters

Some ASCII control characters are widely used, and it is generally not recommended to override them. Additionally, certain keybindings are used by xonsh and may affect functionality if changed.

Keystroke | ASCII control representation | Default command
--- | --- | ---
`Control J` | `<Enter>` | Run command
`Control I` | `<Tab>` | Indent, autocomplete
`Control R` |  | Backwards history search
`Control Z` |  | SIGSTOP current job
`Control C` |  | SIGINT current job

### Useful imports

There are a few useful `prompt_toolkit` tools that will help us create better bindings:

    from prompt_toolkit.keys import Keys
    from prompt_toolkit.filters import Condition, EmacsInsertMode, ViInsertMode

### Custom keyload function

We need our additional keybindings to load after the shell is initialized, so we define a function that contains all of the custom keybindings and decorate it with the appropriate event, in this case `on_ptk_create`.

We’ll start with a toy example that just inserts the text “hi” into the current line of the prompt:

    @events.on_ptk_create
    def custom_keybindings(bindings, **kw):

        @bindings.add(Keys.ControlW)  # or just "c-w" string
        def say_hi(event):
            event.current_buffer.insert_text('hi')

Put that in your xonsh RC, restart xonsh and then see if pressing `Ctrl-w` does anything (it should!)

### What commands can keybindings run?

Pretty much anything! Since we’re defining these commands after xonsh has started up, we can create keybinding events that run subprocess commands with hardly any effort at all. If we wanted to, say, have a command that runs `ls -l` in the current directory:

    from prompt_toolkit.application import run_in_terminal

    @events.on_ptk_create
    def custom_keybindings(bindings, **kw):

        @bindings.add(Keys.ControlP)
        def run_ls(event):
            def _task():
                ls -l
            run_in_terminal(_task)

`run_in_terminal(func)` (imported from `prompt_toolkit.application`) is the canonical `prompt_toolkit` idiom for running code that writes to `STDOUT` from a keybinding. It temporarily hides the prompt, runs your function (which can freely `print` or launch subprocesses), then redraws the prompt — including `$RIGHT_PROMPT` and `$BOTTOM_TOOLBAR` — above the captured output. Do not use `event.cli.renderer.erase()` for this purpose: it resets the renderer’s height bookkeeping and `$BOTTOM_TOOLBAR` (and sometimes `$RIGHT_PROMPT`) will disappear until the next keypress.

### Restrict actions with filters

Often we want a key command to only work if certain conditions are met. For instance, the `<TAB>` key in xonsh brings up the completions menu, but then it also cycles through the available completions. We use filters to create this behavior.

A few helpful filters are included with `prompt_toolkit`, like `ViInsertMode` and `EmacsInsertMode`, which return `True` when the respective insert mode is active.

But it’s also easy to create our own filters that take advantage of xonsh’s beautiful strangeness. Suppose we want a filter to restrict a given command to run only when there are fewer than ten files in a given directory. We just need a function that returns a Bool that matches that requirement and then we decorate it! And remember, those functions can be in xonsh-language, not just pure Python:

    from prompt_toolkit.filters import Condition

    @Condition
    def lt_ten_files():
        return len(g`*`) < 10

Note

See the tutorial section on globbing for more globbing options.

Now that the condition is defined, we can pass it as a `filter` keyword to a keybinding definition:

    @events.on_ptk_create
    def custom_keybindings(bindings, **kw):

        @bindings.add(Keys.ControlL, filter=lt_ten_files)
        def ls_if_lt_ten(event):
            def _task():
                ls -l
            run_in_terminal(_task)

With both of those in your xonsh RC, pressing `Control L` will list the contents of your current directory if there are fewer than 10 items in it. Useful? Debatable. Powerful? Yes.

#### Edit-mode filter

A common use of filters is restricting a binding to a specific editing mode — Emacs insert, Vi insert, or Vi navigation. `prompt_toolkit` ships ready-made filters for each; pass them as `filter` to `bindings.add`:

    from prompt_toolkit.filters import (
        EmacsInsertMode, ViInsertMode, ViNavigationMode,
    )

    @events.on_ptk_create
    def custom_keybindings(bindings, **kw):

        @bindings.add('c-l', filter=EmacsInsertMode())
        def ls_in_emacs_insert(event):
            def _task():
                ls -l
            run_in_terminal(_task)

        @bindings.add('c-l', filter=ViInsertMode() | ViNavigationMode())
        def ls_in_any_vi_mode(event):
            def _task():
                ls -l
            run_in_terminal(_task)

The same key (`Ctrl-L`) can be bound to different actions per mode — `prompt_toolkit` picks the binding whose `filter` is currently true. Combine with custom `Condition` filters using `&`, `|`, `~` (as in `ViInsertMode() & lt_ten_files`) for finer-grained rules.

## Pre-filling the next command

xonsh can pre-fill the prompt input for the next command using two environment variables. This is useful for building interactive workflows, wizards, or keybindings that prepare a command for the user to review and edit before running.

### `$XONSH_PROMPT_NEXT_CMD`

Sets the text that will appear in the next prompt as editable input:

    $XONSH_PROMPT_NEXT_CMD = 'git commit -m ""'

The next time the prompt appears, `git commit -m ""` will be pre-filled and the user can edit it before pressing Enter.

Cursor positioning: Use the `<cursor>` marker to place the cursor at a specific position:

    $XONSH_PROMPT_NEXT_CMD = 'git commit -m "<cursor>"'

The marker is removed from the text and the cursor is placed at its position — in this case, between the quotes.

Example — a keybinding that prepares a git commit:

    @events.on_ptk_create
    def custom_keybindings(bindings, **kw):

        @bindings.add('c-g')
        def prepare_commit(event):
            $XONSH_PROMPT_NEXT_CMD = 'git commit -m "<cursor>"'

Now pressing `Ctrl-G` will pre-fill the next prompt with `git commit -m ""` and place the cursor between the quotes.

### `$XONSH_PROMPT_NEXT_CMD_SUGGESTION`

Sets a greyed-out suggestion (like auto-suggest from history) for the next prompt. The user can accept it by pressing the right arrow key:

    @ $XONSH_PROMPT_NEXT_CMD_SUGGESTION = 'git push origin main'

Unlike `$XONSH_PROMPT_NEXT_CMD`, this does not pre-fill the input — it only shows a suggestion that the user can accept or ignore.

Both variables are cleared automatically after being consumed by the prompt.

### Virtual Environment in Prompt

xonsh obeys the `$VIRTUAL_ENV_DISABLE_PROMPT` environment variable as defined by virtualenv. If this variable is truthy, xonsh will always substitute an empty string for `{env_name}`. Note that unlike other shells, `$VIRTUAL_ENV_DISABLE_PROMPT` takes effect immediately after being set — it is not necessary to re-activate the environment.

xonsh also allows for an explicit override of the rendering of `{env_name}`, via the `$VIRTUAL_ENV_PROMPT` environment variable. If this variable is set to a non-empty value, `{env_name}` will always render as its value, regardless of whether a virtual environment is active. The value is used as-is, without the usual `{env_prefix}` / `{env_postfix}` wrapping — except when it matches the name derived from `$VIRTUAL_ENV` (the `prompt = ...` field in `<venv>/pyvenv.cfg` or the venv directory basename). In that case it is treated as an auto-populated value from `activate.xsh` and wrapped like the other sources, so the prompt reads `(venv) user@host` instead of `venvuser@host`. `$VIRTUAL_ENV_PROMPT` is overridden by `$VIRTUAL_ENV_DISABLE_PROMPT`.

When neither variable is set, `{env_name}` falls back to the active environment’s name — determined, in order, from the `prompt = ...` field in `<venv>/pyvenv.cfg`, from the venv directory name, or from `$CONDA_DEFAULT_ENV`. The detected name is wrapped in `{env_prefix}` and `{env_postfix}` (`(` and ``) `` by default).

For example:

    @ $PROMPT = '{env_name}@ '
    @ source env/bin/activate.xsh
    (env) @ $VIRTUAL_ENV_PROMPT = '~~ACTIVE~~ '
    ~~ACTIVE~~ @ $VIRTUAL_ENV_DISABLE_PROMPT = 1
    @ del $VIRTUAL_ENV_PROMPT
    @ del $VIRTUAL_ENV_DISABLE_PROMPT
    (env) @

## Color theme

You can view the available styles by typing

    @ xonfig styles                      # list styles
    @ xonfig colors paraiso-dark         # review how it looks
    @ $XONSH_COLOR_STYLE='paraiso-dark'  # set a new theme

### Registering custom styles

If you aren’t happy with the styles provided by us (and `pygments`), you can create and register custom styles.

To do so, add something similar to your xonsh RC:

    from xonsh.tools import register_custom_style
    mystyle = {
        "Literal.String.Single": "#ff88aa",
        "Literal.String.Double": "#ff4488",
        "RED": "#008800",
    }
    register_custom_style("mystyle", mystyle, base="monokai")
    $XONSH_COLOR_STYLE = "mystyle"

You can check `xonfig colors` for the token names. The `base` style will be used as a fallback for styles you don’t set - pick one from `xonfig styles` (`default` is used if omitted).

## OSC 7 — Working directory reporting

Xonsh automatically emits OSC 7 escape sequences on every directory change and at shell startup. This is an invisible signal that tells the terminal emulator what the current working directory is.

Terminals use it for:

- Opening new tabs/splits in the same directory

- macOS Terminal.app session restoration after reboot

- Showing the path in the terminal title bar or tab

This works out of the box on most modern terminals including macOS Terminal.app, iTerm2, GNOME Terminal, Windows Terminal, WezTerm, and Kitty. No configuration is needed.

## See also

- Environment Variables – prompt-related environment variables

- Built-in Events – prompt lifecycle events (`on_pre_prompt`, `on_post_prompt`)