Shell Configuration¶
Endo can be customized through a startup configuration file, environment variables, prompt settings, aliases, and key bindings.
Startup File¶
Endo loads ~/.config/endo/init.endo automatically on startup. This file is executed as regular Endo code, so you can use any shell commands or F# expressions.
# ~/.config/endo/init.endo
# Prompt configuration
shell_prompt_preset <- "endo-signature"
shell_prompt_layout <- "two-line"
# Aliases
let ll ...args = & exa -l ...args
let gs ...args = & git status ...args
let gd ...args = & git diff ...args
# Environment
export EDITOR=nvim
export PAGER=less
Tip
Changes to init.endo take effect the next time you start a new Endo session.
To skip loading the startup file entirely, pass --no-profile on the command line:
This is useful for debugging, benchmarking, or when a broken init.endo prevents the shell from starting.
Environment Variables¶
Setting Variables¶
# F#-style export binding
let export PATH = $"/usr/local/bin:{(env 'PATH')?}"
let export EDITOR = "nvim"
let export MY_VAR = "some value"
Reading Variables¶
# Shell-style substitution
echo $HOME
echo ${HOME}
# F#-style (returns Option type)
match (env "HOME") with
| Some dir -> println $"Home: {dir}"
| None -> println "HOME not set"
Special Variables¶
| Variable | Description |
|---|---|
$? | Exit code of the last command |
$$ | PID of the current shell |
$! | PID of the last background process |
$0 | Name of the shell or script |
$1 - $9 | Positional parameters |
Prompt Customization¶
Endo provides a modular prompt system with built-in presets and fine-grained controls. Prompt settings are builtin properties -- assign with <- and read as expressions.
Presets¶
shell_prompt_preset <- "minimal-arrow"
shell_prompt_preset <- "lambda-clean"
shell_prompt_preset <- "powerline"
shell_prompt_preset <- "endo-signature"
shell_prompt_preset <- "dashboard"
Available presets: minimal-arrow, lambda-clean, opencode-bar, powerline, transient, dashboard, boxed-module, gradient-glow, context-adaptive, endo-signature.
Layout¶
# Single line prompt
shell_prompt_layout <- "single-line"
# Two-line prompt (info on top, input below)
shell_prompt_layout <- "two-line"
# Boxed prompt with borders
shell_prompt_layout <- "boxed"
Prompt Indicator¶
The indicator is the character(s) shown just before the input cursor. The string form is rendered verbatim -- there is no template expansion (for example, "${pwd}" prints the literal text ${pwd}).
# Static string form: rendered verbatim, no substitution
shell_prompt_indicator <- "> "
shell_prompt_indicator <- "$ "
To produce an indicator that depends on live shell state, assign a zero-argument function that returns a string. It is invoked at each prompt render:
# Dynamic form: named function
let myPrompt () = $"{(env "PWD")?} => "
shell_prompt_indicator <- myPrompt
# Anonymous lambda literal also works
shell_prompt_indicator <- fun () -> $"{(env "PWD")?} => "
Notes and limitations:
- Invocation runs synchronously on the main thread; results are cached and re-evaluated only when the shell context changes (CWD, exit code, or last- command duration). Within a single context, every keystroke reuses the cached value so typing is never slowed by the callback.
- For genuinely slow callbacks (network or heavy computation), a background- thread execution path analogous to the git status module is still a potential future enhancement. Until then, prefer callbacks that are fast on the happy path.
- If the function is unknown, throws, or returns an empty string, the static
shell_prompt_indicatorvalue is used as a fallback and the shell stays alive. - The static and dynamic forms write to separate storage: assigning a string clears any dynamic function; assigning a function leaves the static string untouched as the fallback.
Separator Style¶
shell_prompt_separator <- "powerline"
shell_prompt_separator <- "arrow"
shell_prompt_separator <- "rounded"
shell_prompt_separator <- "none"
Transient Prompt¶
When enabled, the full prompt is replaced with a compact indicator after a command is submitted, keeping the scrollback clean:
Prompt Spacing¶
Control the number of blank lines above and below the prompt:
# Add a blank line above and below the prompt (default)
shell_prompt_spacing <- 1
# No blank lines around the prompt
shell_prompt_spacing <- 0
Command Duration Threshold¶
Control when the command duration module appears:
Exit Confirmation¶
Control the double Ctrl+D exit confirmation behavior. The first Ctrl+D press shows a hint; only a second press within the timeout exits the shell:
# Set a 2-second confirmation window
shell_exit_confirm_timeout <- 2000
# Disable confirmation (immediate exit on first Ctrl+D)
shell_exit_confirm_timeout <- 0
The default timeout is 1000 ms.
File Listing Icons¶
When listing files with ls, Nerd Font icons are shown next to each filename by default. You can disable icons while keeping colors:
# Disable file icons in ls output
shell_ls_icons <- false
# Re-enable file icons
shell_ls_icons <- true
This requires a Nerd Font to render icons correctly.
Directory Trailing Slash¶
By default, directory names in ls output are shown with a trailing / suffix for easy identification:
# Disable trailing slash on directories
shell_ls_directory_slash <- false
# Re-enable trailing slash (default)
shell_ls_directory_slash <- true
Interactive Mode Detection¶
The read-only property shell_is_interactive indicates whether the shell is running interactively (REPL) or non-interactively (script or -c command):
# In init.endo — only apply interactive settings when appropriate
if shell_is_interactive then
shell_prompt_preset <- "endo-signature"
shell_prompt_layout <- "two-line"
This is useful in init.endo to conditionally apply settings that only make sense in interactive mode (prompt customization, key bindings, etc.). The property is read-only; attempting to assign to it produces a compile-time error.
Prompt Color Customization¶
Endo's prompt colors can be fully customized. Each color property accepts:
"#RRGGBB"-- a solid hex color (e.g.,"#FF6600")"#RRGGBB:#RRGGBB:..."-- a gradient with colon-separated color stops"transparent"-- use the terminal's default background (background only)"theme"-- reset to the current theme's default color
Background Color¶
The prompt background can be set to a solid color, made transparent (showing the terminal's default background), or reset to the theme default:
# Transparent background (terminal default shows through)
shell_prompt_color_background <- "transparent"
# Custom solid background
shell_prompt_color_background <- "#1E1E2E"
# Reset to theme default
shell_prompt_color_background <- "theme"
Tip
Transparent backgrounds work best with the minimal-arrow preset or single-line layouts. Multi-line presets with aurora gradients override the background with their own gradient colors regardless of this setting.
Solid Colors¶
Set any prompt element to a solid hex color:
# Path in bright orange
shell_prompt_color_path <- "#FF6600"
# Green indicator
shell_prompt_color_indicator <- "#50FA7B"
# Red separator bar
shell_prompt_color_separator <- "#FF5555"
Dynamic Colors¶
Any color property (except background) also accepts a zero-argument function that returns a color-spec string. The function is invoked at each context change (cached between renders within the same context), so the color can depend on live shell state such as exit code or working directory:
# Indicator color that changes with the last exit code
let indicatorColor () = "#FF5555"
shell_prompt_color_indicator <- indicatorColor
# Inline lambda form
shell_prompt_color_path <- fun () -> "#5078FF:#00DCC8"
If the function returns an unparseable string or fails, the color falls back to the theme default (same behavior as "theme").
Gradient Colors¶
Any color property (except background) can be set to a multi-stop gradient. Gradient stops are separated by colons. The gradient is interpolated linearly across the text of the corresponding prompt module:
# Blue-to-teal gradient on path (like endo-signature preset)
shell_prompt_color_path <- "#5078FF:#00DCC8"
# Three-stop rainbow gradient on git branch when clean
shell_prompt_color_git_clean <- "#00FF00:#00FFFF:#0000FF"
# Warm gradient for the duration badge
shell_prompt_color_duration <- "#FFB86C:#FF5555"
Available Color Properties¶
| Property | Description |
|---|---|
shell_prompt_color_background | Prompt background (supports transparent) |
shell_prompt_color_path | Current directory path text |
shell_prompt_color_git_clean | Git branch when repository is clean |
shell_prompt_color_git_dirty | Git branch when unstaged changes exist |
shell_prompt_color_git_staged | Git indicator when staged changes exist |
shell_prompt_color_indicator | Input line indicator (e.g., |>) |
shell_prompt_color_indicator_error | Indicator when last command failed |
shell_prompt_color_exit_code | Exit code badge |
shell_prompt_color_duration | Command duration badge |
shell_prompt_color_hostname | Hostname (shown in SSH sessions) |
shell_prompt_color_separator | Left bar / separator |
shell_prompt_color_badge | Badge background (F# mode, structured output) |
shell_prompt_color_badge_text | Badge text |
shell_prompt_color_clock | Clock module text |
Preset Interaction¶
Color overrides are applied on top of the selected preset. When you switch presets, all color overrides are reset to the preset's defaults:
# Start with a preset, then customize individual colors
shell_prompt_preset <- "endo-signature"
shell_prompt_color_background <- "transparent"
shell_prompt_color_path <- "#FF6600:#FFFF00"
# Switching presets resets all overrides
shell_prompt_preset <- "powerline" # colors reset to powerline defaults
Example: Custom Theme¶
# ~/.config/endo/init.endo
# Start with a clean base
shell_prompt_preset <- "opencode-bar"
# Custom color scheme
shell_prompt_color_background <- "#1A1B26"
shell_prompt_color_path <- "#7AA2F7:#BB9AF7"
shell_prompt_color_git_clean <- "#9ECE6A"
shell_prompt_color_git_dirty <- "#F7768E"
shell_prompt_color_separator <- "#565F89"
shell_prompt_color_indicator <- "#7DCFFF"
shell_prompt_color_indicator_error <- "#F7768E"
Reading Property Values¶
All prompt properties can be read as expressions:
# Print current preset
print shell_prompt_preset
# Print current path color
print shell_prompt_color_path
# Use in conditionals
if shell_prompt_spacing == 0 then println "Compact mode"
Aliases¶
Endo uses F# function definitions with variadic parameters as aliases:
# Define aliases with splat syntax
let ll ...args = & exa --long --group --header ...args
let la ...args = & exa -la ...args
let gs ...args = & git status ...args
let gco ...args = & git checkout ...args
These aliases support argument passthrough -- any additional arguments you provide are forwarded to the underlying command.
# Usage:
ll # runs: exa --long --group --header
ll /tmp # runs: exa --long --group --header /tmp
gs -s # runs: git status -s
Key Bindings¶
The bind Builtin¶
Use the bind command to view and modify key bindings at runtime:
# List all current bindings
bind
# Bind a key to an action
bind ctrl+y yank
# Remove a binding
bind -r ctrl+y
# Reset to defaults
bind --reset
# Show available actions and key format
bind --help
Key Format¶
Keys are specified as modifier+key combinations:
ctrl+a,ctrl+shift+aalt+f,alt+bshift+left,shift+righthome,end,deletef1throughf12
Default Bindings¶
Undo / Redo¶
| Key | Action |
|---|---|
ctrl+z | Undo |
ctrl+y | Redo |
ctrl+shift+z | Redo |
Clipboard & Selection¶
| Key | Action |
|---|---|
ctrl+c | Copy (or interrupt if no selection) |
ctrl+x | Cut |
ctrl+shift+a | Select all |
Tip
Paste has no default binding. Terminal paste is handled natively via the terminal's paste event (ctrl+v or ctrl+shift+v depending on terminal). To bind the kill ring paste action, run: bind ctrl+v paste
Movement¶
| Key | Action |
|---|---|
ctrl+a | Smart move to line start (toggles between first non-space and column 0) |
ctrl+e | Smart move to line end |
ctrl+f | Move forward one character |
ctrl+b | Move backward one character |
alt+f | Move forward one word |
alt+b | Move backward one word |
ctrl+p | Move up one line |
ctrl+n | Move down one line |
left | Move backward one character |
right | Move forward one character |
ctrl+left | Move backward one word |
ctrl+right | Move forward one word |
up | Move up / history previous |
down | Move down / history next |
home | Move to line start |
end | Move to line end |
ctrl+home | Move to buffer start |
ctrl+end | Move to buffer end |
Editing¶
| Key | Action |
|---|---|
backspace | Delete character backward |
delete | Delete character forward |
ctrl+d | Delete character forward (EOF on empty line) |
ctrl+backspace | Delete word backward |
alt+backspace | Delete word backward |
ctrl+w | Delete word backward |
alt+d | Delete word forward |
ctrl+k | Kill to end of line |
ctrl+u | Kill to start of line |
Tip
Transpose has no default binding. To bind it, run: bind ctrl+t transpose
Agent Mode¶
| Key | Action |
|---|---|
ctrl+t | Enter agent mode |
shift+tab | Cycle agent sub-modes |
ctrl+/ | Cycle thinking modes |
ctrl+. | Cycle model |
Kill Ring¶
| Key | Action |
|---|---|
alt+y | Yank pop (cycle through kill ring) |
Tip
Yank has no default binding (it was ctrl+y which is now Redo). To restore the Emacs-style binding, run: bind ctrl+y yank
Control¶
| Key | Action |
|---|---|
enter | Submit command |
shift+enter | Insert newline (multiline editing) |
alt+enter | Insert newline (multiline editing) |
tab | Trigger completion |
ctrl+space | Trigger completion (always shows popup) |
ctrl+l | Clear screen |
ctrl+r | History search |
right / end / ctrl+e | Accept ghost text suggestion (when at end of line) |
Action Reference¶
All bindable action names for use with bind:
| Category | Actions |
|---|---|
| Movement | move-forward-char, move-backward-char, move-forward-word, move-backward-word, move-to-line-start, move-to-line-end, move-to-buffer-start, move-to-buffer-end, move-up, move-down, smart-move-to-line-start, smart-move-to-line-end |
| Editing | delete-char-backward, delete-char-forward, delete-word, delete-word-backward, kill-to-end, kill-to-start, transpose |
| Undo / Redo | undo, redo |
| Kill Ring | yank, yank-pop |
| Selection | select-all |
| Clipboard | cut, copy, paste |
| Control | submit, abort, insert-newline, agent-mode, cycle-agent-mode, cycle-thinking-mode, cycle-model |
| History | history-prev, history-next |
Under Development
Some configuration features (such as vi mode) are still under development. See the Roadmap for planned features.