# Configuration Reference 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. ## Sections ### `[trigger]` 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. | ```toml [trigger] auto_chars = [' ', '/', '-', '.'] delay_ms = 150 auto_trigger = true ``` ### `[popup]` Controls 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]`. | | `tab_accepts_top` | bool | `false` | When `true`, the accept key (Tab) accepts the top-ranked suggestion even when you haven't navigated yet, instead of forwarding a literal tab to the shell. Restores the Fig/Kiro "type, glance, Tab" flow without the extra arrow-key press. Only the `accept` action is affected: with the default bindings the `accept_and_enter` action (Enter) is a separate binding and still runs the command line, so a stray Enter never silently accepts the top suggestion. (If you rebind the `accept` action itself onto Enter, Enter becomes the accept key and will accept the top item.) | ```toml [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 = 80 tab_accepts_top = false ``` Popup 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. ### `[suggest]` 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. | | `match_mode` | string | `"fuzzy"` | How the typed query filters candidates. `"fuzzy"` matches characters as an in-order subsequence (`gco` → `git checkout`); `"substring"` requires the characters to appear contiguously (`cl` → `clone`, `include`, but not `calendar`). Space-separated words are matched as independent substrings. Requires a restart. | ```toml [suggest] max_results = 50 max_history_results = 5 generator_timeout_ms = 5000 match_mode = "fuzzy" # or "substring" for contiguous matching ``` Shell history loads up to 10,000 entries. ### `[suggest.spec_cache]` 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: ```toml [suggest.spec_cache] idle_ttl_secs = 300 keep_warm = ["git", "cd", "ls", "cargo", "npm", "docker"] max_resident_mb = 100 ``` Trade-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. ### `[suggest.providers]` 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. | ```toml [suggest.providers] commands = true filesystem = true specs = true git = true js_runtime = true ``` ### `[paths]` Override 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 `/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. | ```toml [paths] spec_dirs = ["~/.config/ghost-complete/specs", "/usr/local/share/ghost-complete/specs"] ``` ### `[keybindings]` 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 | ```toml [keybindings] accept = "tab" accept_and_enter = "enter" dismiss = "escape" navigate_up = "arrow_up" navigate_down = "arrow_down" trigger = "ctrl+/" ``` #### Key Name Syntax - Lowercase letters: `a` through `z` - Special keys: `tab`, `enter`, `escape`, `backspace`, `space` - Arrow keys: `arrow_up`, `arrow_down`, `arrow_left`, `arrow_right` - Modifiers: `ctrl+` (e.g., `ctrl+space`, `ctrl+/`) ### `[theme]` 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 | ```toml [theme] preset = "catppuccin" # Override individual fields from the preset: match_highlight = "underline" feedback_error = "dim fg:#d20f39" ``` #### Presets | 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. #### Style String Syntax 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 ### `[experimental]` Opt-in features that are not yet considered stable. | Field | Type | Default | Description | |-------|------|---------|-------------| | `multi_terminal` | bool | `false` | Enable unsupported/unknown terminals. All 10 supported terminals (Ghostty, Otty, 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. | ```toml [experimental] multi_terminal = true aws_sdk_provider = false aws_sdk_fallback_to_cli = true brew_search_cap = 1000 ``` `aws_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, Otty, Kitty, WezTerm, Rio, Zed** — DECSET 2026 synchronized output, native OSC 133 prompt markers. Otty is a Ghostty fork (`TERM_PROGRAM=otty`) and inherits Ghostty's exact capability profile; unlike Ghostty proper it does not set `GHOSTTY_RESOURCES_DIR`, so it is detected purely via `TERM_PROGRAM`. - **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=1` is 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). ## Logging Ghost Complete logs through the `tracing` crate. Logging is configured via CLI flags and the `RUST_LOG` environment variable, not via `config.toml`. ### CLI Flags | Flag | Default | Description | |------|---------|-------------| | `--log-level ` | `warn` | One of `trace`, `debug`, `info`, `warn`, `error`. Ignored when `RUST_LOG` is set. | | `--log-file ` | (see below) | Write logs to this file. When unset in proxy mode, the default path is used. | ### Default Log File 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. ### Level Hierarchy ``` 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` Precedence `RUST_LOG` is read first and overrides `--log-level` when both are set. It uses the standard [`tracing-subscriber` `EnvFilter` syntax](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html), which supports per-crate, per-module, and per-span filters. Examples: ```bash # 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-complete ``` Crate names use underscores (e.g. `gc_suggest`), not hyphens. Filter directives are comma-separated; the first bare level (if any) sets the global default. ### Tail-f Recipe Open the log in a second terminal while reproducing an issue: ```bash tail -f "${XDG_STATE_HOME:-$HOME/.local/state}/ghost-complete/ghost-complete.log" ``` ### Generating a Bug Report 1. Start the proxy with verbose logging: `ghost-complete --log-level debug`. 2. Reproduce the bug in that session. 3. 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. ## Full Example ```toml [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" ``` ## Notes - **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](https://www.nerdfonts.com/) for the best experience. - **History control:** Use `max_history_results` (not `providers.history`) to control history. Set to `0` to 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. ### Hot-Reload Behavior | 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`, `tab_accepts_top` | Yes | | `[suggest]` | All fields | No | | `[suggest.providers]` | All fields | No | | `[suggest.spec_cache]` | 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).