Ghost Complete reads its configuration from ~/.config/ghost-complete/config.toml. All fields are optional — unset values use their defaults.
Run ghost-complete install to generate a default config with all fields documented as comments.
Controls when the autocomplete popup appears.
| Field | Type | Default | Description |
|---|---|---|---|
auto_chars |
char[] | [' ', '/', '-', '.'] |
Characters that trigger suggestion after typing |
delay_ms |
integer | 150 |
Milliseconds to wait after typing pauses before showing suggestions. Set to 0 to disable debounce (trigger immediately). |
auto_trigger |
boolean | true |
When false, disables all automatic popup triggers. Only the manual keybinding opens the popup. |
[trigger]
auto_chars = [' ', '/', '-', '.']
delay_ms = 150
auto_trigger = trueControls the popup appearance.
| Field | Type | Default | Description |
|---|---|---|---|
max_visible |
integer | 10 |
Maximum number of suggestions shown at once |
borders |
bool | false |
Draw a border around the popup |
feedback_dismiss_ms |
integer | 1200 |
Milliseconds to keep Empty/Error feedback visible. Set to 0 to disable auto-dismiss. Values above 10000 are clamped. |
spinner |
bool | true |
Animate Loading feedback when the popup is wide enough |
show_provider_errors |
bool | false |
Show provider names in error feedback. Disabled by default for shared-screen privacy. |
render_block_ms |
integer | 80 |
Maximum time in milliseconds to block before painting sync results while waiting for the first high-priority async generator. Set to 0 to paint immediately. Clamped to [0, 300]. |
min_width |
integer | 20 |
Lower bound for popup width in display columns. Clamped to [10, 500]. If max_width is lower after normalization, max_width is raised to min_width. |
max_width |
integer | 60 |
Upper bound for popup width in display columns. Clamped to [min_width, 500] and additionally to the live screen_cols at render time. Bump this on wide terminals to give descriptions more room before the truncation ellipsis (…) kicks in. |
description_box |
string | "off" |
Adjacent description box mode. "off" keeps the legacy inline-truncated behavior. "side" renders a wrapped multi-line box next to the main popup for the selected suggestion when the inline description would be hidden or truncated. The box is capped by description_box_lines and available rows; short descriptions that already fit don't trigger it. Falls back to a stacked-below box when there's no horizontal room, and to inline truncation when neither fits. |
description_box_max_width |
integer | 60 |
Maximum width (display columns) for the description box. Clamped to [20, 200]. The actual rendered width adapts to the columns remaining beside the main popup. |
description_box_lines |
integer | 5 |
Maximum wrapped lines in the description box. Long descriptions are hard-truncated with an ellipsis on the final line. 0 resets to default 5; values above 20 are clamped to 20. |
description_box_debounce_ms |
integer | 80 |
Debounce window (ms) for description-box updates on selection change. Holding arrow keys causes the box to update at most once per window, avoiding flicker. Set to 0 to disable debounce. Clamped to [0, 500]. |
[popup]
max_visible = 10
borders = false
feedback_dismiss_ms = 1200
spinner = true
show_provider_errors = false
render_block_ms = 80
min_width = 20
max_width = 60
description_box = "off"
description_box_max_width = 60
description_box_lines = 5
description_box_debounce_ms = 80Popup width is content-driven (sized to the longest visible suggestion) and clamped to [min_width, max_width]. Descriptions that don't fit are truncated with a single-column ellipsis (…). Set description_box = "side" to surface a wrapped description when the inline description would be hidden or truncated. The box is capped by description_box_lines and available rows without permanently widening the main popup.
Controls the suggestion engine behavior.
| Field | Type | Default | Description |
|---|---|---|---|
max_results |
integer | 50 |
Maximum total candidates to consider |
max_history_results |
integer | 5 |
Maximum history entries shown in popup. Set to 0 to disable history. |
generator_timeout_ms |
integer | 5000 |
Per-invocation timeout (milliseconds) for script and git generators. |
[suggest]
max_results = 50
max_history_results = 5
generator_timeout_ms = 5000Shell history loads up to 10,000 entries.
Cache eviction policy for parsed completion specs. Eviction is opt-in; the
default (idle_ttl_secs = 0) preserves the lazy-loading layer's "parse
once, hold forever" behavior.
| Field | Type | Default | Description |
|---|---|---|---|
idle_ttl_secs |
integer | 0 |
Seconds after last access before a successfully parsed spec is evicted. 0 disables eviction entirely. |
sweep_interval_secs |
integer | 60 |
How often the background sweep wakes to scan for idle entries. Ignored when idle_ttl_secs = 0. |
keep_warm |
string[] | [] |
Spec aliases that must never be evicted. Aliases match filename stems and CompletionSpec.name values, not shell aliases. |
max_resident_mb |
integer | 0 |
LRU backstop: after TTL eviction, if total estimated resident heap exceeds this cap, evict more entries oldest-access-first until under cap. 0 disables the backstop. |
Recommended recipe:
[suggest.spec_cache]
idle_ttl_secs = 300
keep_warm = ["git", "cd", "ls", "cargo", "npm", "docker"]
max_resident_mb = 100Trade-off: enabling eviction means a re-parse cost on the next access of an evicted spec (~150 ms in the AWS worst case, <5 ms for most specs). Shell forwarding is unaffected; only the popup for that one keystroke is delayed.
Enable this when daemon idle resident memory matters: long-running shells, modest-RAM machines, or multiple terminals. The default disables eviction because the lazy-loading layer already keeps idle daemon memory low until a heavy spec has been parsed.
Hot reload is not supported. Restart the daemon after changing this section.
Enable or disable individual suggestion providers.
| Field | Type | Default | Description |
|---|---|---|---|
commands |
bool | true |
$PATH command completions |
filesystem |
bool | true |
File and directory completions |
specs |
bool | true |
Fig-compatible JSON spec completions |
git |
bool | true |
Git context completions (branches, tags, remotes) |
js_runtime |
bool | true |
QuickJS evaluator for requires_js spec generators. Set false to disable JS-backed post_process, script_function, custom, and token_only generators while keeping static spec completions. |
[suggest.providers]
commands = true
filesystem = true
specs = true
git = true
js_runtime = trueOverride default file paths.
| Field | Type | Default | Description |
|---|---|---|---|
spec_dirs |
string[] | [] |
Directories to load completion specs from. Supports ~ expansion. When empty, the loader auto-detects in this order: ~/.config/ghost-complete/specs/, then <exe-dir>/specs, then ./specs, with the binary's embedded specs appended as a lowest-precedence safety net. When set, the configured directories are used instead — but if all of them are missing or not directories, the loader falls back to the same auto-detection chain. Read errors from existing configured directories are reported by the spec loader and do not trigger auto-detection fallback. |
[paths]
spec_dirs = ["~/.config/ghost-complete/specs", "/usr/local/share/ghost-complete/specs"]Customize keyboard shortcuts. Each value is a key name string. Invalid key names cause a startup error (fail-fast).
| Field | Type | Default | Description |
|---|---|---|---|
accept |
string | "tab" |
Accept the selected suggestion |
accept_and_enter |
string | "enter" |
Accept and execute |
dismiss |
string | "escape" |
Dismiss the popup |
navigate_up |
string | "arrow_up" |
Move selection up |
navigate_down |
string | "arrow_down" |
Move selection down |
trigger |
string | "ctrl+/" |
Manually trigger completions |
[keybindings]
accept = "tab"
accept_and_enter = "enter"
dismiss = "escape"
navigate_up = "arrow_up"
navigate_down = "arrow_down"
trigger = "ctrl+/"- Lowercase letters:
athroughz - Special keys:
tab,enter,escape,backspace,space - Arrow keys:
arrow_up,arrow_down,arrow_left,arrow_right - Modifiers:
ctrl+<key>(e.g.,ctrl+space,ctrl+/)
Customize popup colors and styles. Values are space-separated SGR token strings. Invalid styles cause a startup error (fail-fast). Changes are applied live when config hot-reload is active.
| Field | Type | Default | Description |
|---|---|---|---|
preset |
string | "" |
Base preset: dark, light, catppuccin, material-darker. Empty uses dark. Field overrides below take priority over preset values. |
selected |
string | (from preset) | Style for the selected (highlighted) item |
description |
string | (from preset) | Style for suggestion descriptions |
match_highlight |
string | (from preset) | Style for fuzzy-matched characters |
item_text |
string | (from preset) | Style for non-selected item text |
scrollbar |
string | (from preset) | Style for the scrollbar track |
border |
string | (from preset) | Style for the popup border |
feedback_loading |
string | (from preset) | Style for Loading feedback |
feedback_empty |
string | (from preset) | Style for Empty feedback |
feedback_error |
string | (from preset) | Style for provider Error feedback |
[theme]
preset = "catppuccin"
# Override individual fields from the preset:
match_highlight = "underline"
feedback_error = "dim fg:#d20f39"| Preset | Selected | Description | Match Highlight | Item Text | Scrollbar | Border | Feedback Error |
|---|---|---|---|---|---|---|---|
dark |
reverse |
dim |
bold |
(none) | dim |
dim |
dim fg:#f38ba8 |
light |
fg:#1e1e2e bg:#dce0e8 bold |
fg:#6c6f85 |
fg:#d20f39 bold |
(none) | fg:#9ca0b0 |
fg:#9ca0b0 |
dim fg:#d20f39 |
catppuccin |
fg:#cdd6f4 bg:#585b70 bold |
fg:#6c7086 |
fg:#f9e2af bold |
(none) | fg:#585b70 |
fg:#585b70 |
dim fg:#f38ba8 |
material-darker |
fg:#eeffff bg:#424242 bold |
fg:#616161 |
fg:#ffcb6b bold |
(none) | fg:#424242 |
fg:#424242 |
dim fg:#ff5370 |
All presets leave item_text unstyled (default terminal foreground). feedback_loading and feedback_empty inherit description by default. Override item_text to colorize non-selected items.
Styles are space-separated tokens:
| Token | Effect |
|---|---|
bold |
Bold text |
dim |
Dim/faint text |
underline |
Underlined text |
reverse |
Swap foreground/background |
fg:N |
Set foreground to 256-color index N (0-255) |
bg:N |
Set background to 256-color index N (0-255) |
fg:#RRGGBB |
Set foreground to 24-bit truecolor |
bg:#RRGGBB |
Set background to 24-bit truecolor |
Examples:
"reverse"— inverted colors (default selected style)"bold fg:255"— bold white text"dim"— faint text (default description style)"fg:#cdd6f4 bg:#585b70 bold"— Catppuccin-style selection"bold underline fg:208"— bold underlined orange text
Opt-in features that are not yet considered stable.
| Field | Type | Default | Description |
|---|---|---|---|
multi_terminal |
bool | false |
Enable unsupported/unknown terminals. All 9 supported terminals (Ghostty, Kitty, WezTerm, Alacritty, Rio, iTerm2, Terminal.app, Zed, VSCode) work without this flag. Set to true only if you want to try Ghost Complete on an unlisted terminal. |
aws_sdk_provider |
bool | false |
Opt in to native AWS SDK completions. Default false means Ghost Complete does not make outbound AWS SDK calls. |
aws_sdk_fallback_to_cli |
bool | true |
When native AWS SDK completions are enabled but cannot run, allow the existing aws CLI script path to produce slower fallback completions. |
brew_search_cap |
integer | 1000 |
Ceiling on brew search "" results considered by the searchable-formulae provider. Lower this on slower machines; raise for broader unfiltered exploration. |
[experimental]
multi_terminal = true
aws_sdk_provider = false
aws_sdk_fallback_to_cli = true
brew_search_cap = 1000aws_sdk_provider is intentionally default-off for the first AWS SDK release
because it can make HTTPS calls to AWS endpoints while completing AWS
arguments. Static completions and existing script-based AWS completions remain
available without enabling it.
aws_sdk_fallback_to_cli is independent. Keep it enabled if you have the AWS
CLI installed and want completions to continue when SDK credentials, profile
resolution, region configuration, or network access are unavailable. Set it to
false only if you prefer SDK-backed completions to fail closed instead of
shelling out to aws.
The AWS SDK provider uses the normal AWS credential/profile environment:
AWS_ACCESS_KEY_ID plus AWS_SECRET_ACCESS_KEY, optional
AWS_SESSION_TOKEN, AWS_PROFILE or AWS_DEFAULT_PROFILE, and
AWS_REGION or AWS_DEFAULT_REGION. It also reads profile names and regions
from ~/.aws/config and ~/.aws/credentials, or from AWS_CONFIG_FILE and
AWS_SHARED_CREDENTIALS_FILE when those are set. ghost-complete doctor
reports the visible AWS env/file state without making live AWS calls.
Ghost Complete auto-detects the terminal via TERM_PROGRAM and terminal-specific env vars (KITTY_WINDOW_ID, WEZTERM_UNIX_SOCKET, ALACRITTY_SOCKET, ZED_TERM, VSCODE_IPC_HOOK_CLI), then selects the appropriate rendering strategy:
- Ghostty, Kitty, WezTerm, Rio, Zed — DECSET 2026 synchronized output, native OSC 133 prompt markers.
- VSCode (and forks: VSCodium, Cursor, Windsurf, Positron, Trae) — DECSET 2026 synchronized output via xterm.js, native OSC 133. Coexists with VSCode's own shell integration: the proxy forwards the editor's OSC 633 sequences untouched so command decorations / sticky scroll / "run recent command" keep working, and Ghost Complete's own shell integration suppresses its redundant OSC 7771 emission when
VSCODE_INJECTION=1is set. - Alacritty — DECSET 2026 synchronized output, OSC 7771 shell integration prompt markers (Alacritty does not support OSC 133).
- iTerm2 / Terminal.app — pre-render buffer (single
write()atomicity), OSC 7771 shell integration prompt markers.
tmux support: Ghostty, Kitty, WezTerm, Alacritty, iTerm2, Zed, and VSCode are detected inside tmux via their respective env vars. Terminal.app inside tmux is not detected (it sets no env var that leaks through tmux).
Ghost Complete logs through the tracing crate. Logging is configured via CLI flags and the RUST_LOG environment variable, not via config.toml.
| Flag | Default | Description |
|---|---|---|
--log-level <level> |
warn |
One of trace, debug, info, warn, error. Ignored when RUST_LOG is set. |
--log-file <path> |
(see below) | Write logs to this file. When unset in proxy mode, the default path is used. |
In proxy mode (ghost-complete wrapping the shell), logs default to a file — never stderr — to avoid corrupting the terminal stream. The default path is:
$XDG_STATE_HOME/ghost-complete/ghost-complete.log
When XDG_STATE_HOME is unset, it falls back to:
~/.local/state/ghost-complete/ghost-complete.log
The parent directory is created automatically on startup. If directory creation fails, Ghost Complete prints a one-line warning to stderr and falls back to stderr logging for the duration of that run.
Subcommands (status, doctor, validate-specs, config, install, uninstall) log to stderr by default; pass --log-file to redirect them.
error < warn < info < debug < trace
Setting --log-level info enables info, warn, and error events. trace is the most verbose and includes every internal decision point.
RUST_LOG is read first and overrides --log-level when both are set. It uses the standard tracing-subscriber EnvFilter syntax, which supports per-crate, per-module, and per-span filters.
Examples:
# Everything at debug or higher
RUST_LOG=debug ghost-complete
# Debug only in the suggest engine; everything else at warn
RUST_LOG=warn,gc_suggest=debug ghost-complete
# Debug in the suggest engine and info in the PTY loop
RUST_LOG=gc_suggest=debug,gc_pty=info ghost-complete
# Trace a single module
RUST_LOG=gc_parser::osc=trace ghost-completeCrate names use underscores (e.g. gc_suggest), not hyphens. Filter directives are comma-separated; the first bare level (if any) sets the global default.
Open the log in a second terminal while reproducing an issue:
tail -f "${XDG_STATE_HOME:-$HOME/.local/state}/ghost-complete/ghost-complete.log"- Start the proxy with verbose logging:
ghost-complete --log-level debug. - Reproduce the bug in that session.
- Attach
$XDG_STATE_HOME/ghost-complete/ghost-complete.log(or the fallback path) to the GitHub issue.
For crate-targeted investigations, combine --log-level with RUST_LOG, e.g. RUST_LOG=gc_pty=trace ghost-complete to inspect the PTY loop in isolation.
[trigger]
auto_chars = [' ', '/', '-']
delay_ms = 200
[popup]
max_visible = 8
[suggest]
max_results = 100
max_history_results = 3
[suggest.providers]
commands = true
filesystem = true
specs = true
git = false
js_runtime = true
[paths]
spec_dirs = ["~/.config/ghost-complete/specs"]
[keybindings]
accept = "tab"
accept_and_enter = "enter"
dismiss = "escape"
trigger = "ctrl+/"
[theme]
preset = "catppuccin"
match_highlight = "underline"- Config hot-reload: Some fields are applied live without restarting your shell. Others require a shell restart. See the table below.
- Nerd Font icons: The popup gutter uses Nerd Font icons. If your terminal font doesn't include Nerd Font patches, you'll see placeholder characters. Use a Nerd Font for the best experience.
- History control: Use
max_history_results(notproviders.history) to control history. Set to0to disable history entirely. - Popup navigation: PageUp, PageDown, Home, and End navigate the popup when it is visible and are forwarded to the shell when it is hidden. These structural keys are not user-configurable.
| Section | Fields | Live Reload |
|---|---|---|
[theme] |
All fields | Yes |
[keybindings] |
All fields | Yes |
[trigger] |
auto_chars |
Yes |
[trigger] |
delay_ms |
No |
[trigger] |
auto_trigger |
Yes |
[popup] |
max_visible, borders, feedback_dismiss_ms, spinner, show_provider_errors, render_block_ms, min_width, max_width, description_box, description_box_max_width, description_box_lines, description_box_debounce_ms |
Yes |
[suggest] |
All fields | No |
[suggest.providers] |
All fields | No |
[paths] |
All fields | No |
[experimental] |
All fields | No |
Fields marked "No" require a shell restart (source ~/.zshrc or open a new terminal).