A one-command terminal setup for macOS and Linux — themes, prompts, fonts, and tools all in sync.
English · 简体中文
Pick a theme — slate previews the whole stack live, no reload.
I could never find a terminal-beautification tool that actually fit the way I use my machine. Every time I wanted a nice setup, I ended up chasing dotfile repos, copy-pasting snippets, and stacking plugins on top of plugins. After all that effort the environment would usually end up a mess, and when I needed to recover I had to dig through everything to figure out what had actually been changed.
So I wrote slate. One command sets up a coordinated look across your terminal, prompt, fonts, and CLI tools. Colors use managed files with targeted edits to existing configuration. Use slate clean to remove integration, or a restore point to recover earlier personal settings.
# macOS — Homebrew
brew install MoonMao42/tap/slate-cli
# macOS or Linux — install script
curl -fsSL https://raw.githubusercontent.com/MoonMao42/slate/main/install.sh | sh
# Rust users
cargo install slate-cliThen run slate to open the menu. Use slate setup when you want the full terminal, font, and shell integration workflow.
To upgrade a script-installed copy, rerun the install script. It verifies the
archive's SHA-256, stages the new executable beside the destination, then replaces
the old file atomically. Failures before the final rename leave the previous
executable in place. An interruption at the rename boundary may leave either the
old or the complete new executable. It does not run the downloaded binary or change
your Slate configuration. A successful upgrade does not keep a backup executable.
If the destination is a symlink, use the package manager that owns it (for Homebrew,
brew upgrade slate-cli) or choose a separate directory with SLATE_INSTALL_DIR.
Run slate --help and check that your executable offers tools and prompt.
This guide describes this branch; older installed releases may lack these entries.
If a command is missing, check which binary your terminal runs (command -v slate
on macOS/Linux) before rerunning full setup.
- Browse without applying:
slate prompt --listshows five illustrative layouts;slate toolsopens tool discovery. Opening details neither installs software nor syncs colors. Samples are not screenshots of your current terminal. - Change only the prompt layout: inspect
slate prompt classic --dry-run, then useslate prompt classicto review and confirm. This needs a saved theme and the relevant integration; it does not install tools. - Sync one tool: start with
slate tools info btop, thenslate tools sync btop --dry-run. Review the paths before confirmingslate tools sync btop. The tool must be detected and a theme saved; sync does not install software or edit shell startup files. During confirmation, changes to target file contents, permissions or identity invalidate the review. Reads are bounded to 8 MiB per file and 64 MiB total; this recheck is not an atomic lock against external editors. - Undo a change: find its recovery point with
slate restore --list, inspectslate restore <ID> --dry-run, then enter the restore flow withslate restore <ID>. Replace<ID>with the actual listed ID. File recovery does not automatically undo derived caches or running application state.
Live preview in slate theme may temporarily write to several detected adapters;
it is not read-only browsing. Esc restores preview files. slate clean removes
integration rather than restoring your previous personal configuration. Change
one thing, check its actual application behavior, then move to the next.
-
One palette across Ghostty, Kitty, Alacritty, Neovim, Starship, bat, btop, Yazi, Zellij, delta, ls, eza, lazygit, fastfetch, tmux, and zsh-syntax-highlighting.
-
🌓 Auto dark/light pairing — native watcher on macOS, XDG Desktop Portal (with GNOME fallback) on Linux.
-
Colors live in managed files, with small integration edits to your existing configuration. Snapshots and read-only restore previews help you inspect and recover changes.
-
One visual language across every command. Headings, severity markers, and tree receipts all flow through the same render contract, so
slate setup,slate status, and an error message all look like they came from the same tool. -
Small sounds on theme apply, picker navigation, setup completion, and errors. Quiet by design; turn it off with
slate config set sound off. (The README recordings are silent; runtime feedback is included.)
Terminal, prompt, system info, CLI utilities — same palette everywhere.
slate list — 9 family bands, Solarized landing right after Catppuccin.
* GitHub README recordings render silently; the installed CLI still plays the curated runtime SFX unless disabled.
Zsh syntax highlighting uses native token styles for commands, quoted arguments,
options, paths and unknown tokens. It updates named color entries without replacing
the highlighter list or clearing unrelated custom styles. It also preserves
existing underline, bold and background attributes while replacing
foregrounds; a none reset is removed so it cannot cancel the selected foreground.
Repeated sourcing does not accumulate attributes or leak helper variables. Native checks
cover builtin, quoted-string, comment and unknown-command regions across all palettes;
other token contexts and the live prompt remain unverified. Existing shells need
to load the updated snippet before the colors change.
tools sync zsh-syntax-highlighting updates only its managed snippet, preserving
permissions and avoiding identical rewrites. It never sources the plugin or edits
.zshrc; its file restore point covers the snippet, not a running shell's styles.
Comment colors target 4.5:1 against the palette's opaque background, preserving
the original shade when sufficient and using a theme gray/body fallback otherwise.
This is not a contrast guarantee for transparent windows or personal overrides.
slate tools sync fastfetch --dry-run reviews one managed config.jsonc.
It is Slate's preset layout with theme colors, not a merge of personal modules
or logos. Sync preserves personal configuration and shell startup; it updates only
the managed preset, keeps its permissions and skips identical content. The file
restore point covers this preset, not shell variables or installed packages.
The existing Slate shell wrapper must select it; generation is not activation proof.
Updated Bash/Zsh/Fish wrappers omit the managed -c argument if the preset is
missing, unreadable, nonregular or a final symlink, leaving native config selection
and user arguments intact. This requires regenerated shell integration; palette-only
sync does not install the wrapper change.
Explicit -c/--config arguments bypass Slate's injected config argument, since
fastfetch accepts only one config file.
Help, version and read-only --list-* discovery flags also bypass the injected
preset, so a damaged Slate config does not block these commands. Config generation
and ordinary rendering are not treated as read-only discovery.
Native fastfetch 2.68.1 rendered the generated key, separator and output colors
for all bundled palettes in a fixed-text fixture. That check replaces system
modules and disables the logo; it does not verify the full system-information
layout, logo rendering or your shell's integration.
Eza's managed theme.yml uses native filekinds, perms, size, links, users, git and metadata
style objects, without setting icons or background colors. A fresh shell loads
Slate's EZA_CONFIG_DIR and color exports. EZA_COLORS/LS_COLORS may override
file colors; successful file generation alone does not prove the launch environment
uses it. Native eza 0.23.5 output was checked for directories, regular files,
executables and symlinks across all bundled palettes in isolated profiles.
tools sync eza --dry-run reviews one managed file. Applying preserves its mode,
skips identical bytes and rejects unsafe or oversized existing files. It does
not rewrite shell startup or personal eza configuration; the sync restore point
covers that one palette file, not package installation or live shell variables.
Configuration lookup captures EZA_CONFIG_DIR; SLATE_HOME ignores this host
override and uses its isolated .config/eza. A custom selection is not a sync
destination: generated colors remain under Slate's managed directory.
Run slate doctor eza from the shell used to launch eza, or choose Check Theme
Setup in its tool page. This read-only check distinguishes managed palette
matches, captured directory selection and the presence of EZA_COLORS/LS_COLORS
overrides. It never launches eza, reads personal YAML or evaluates color expressions;
directory aliases, shell startup and actual rendering remain unverified.
If the captured directory selects Slate but its palette is missing, the check
flags that separately from an unreadable file and never recreates it automatically.
Lazygit integration generates GUI colors only; personal layout, keybindings and
pagers are retained. Updated shell integration combines readable files using
Lazygit's comma-separated LG_CONFIG_FILE, placing personal settings last. An
explicit custom LG_CONFIG_FILE is left untouched and can bypass Slate's colors.
Reopen Lazygit from a new shell after applying. tools sync lazygit updates only
the palette fragment; older shell integration needs regeneration through reviewed
slate setup. Parser validation is not a live appearance check.
Run slate doctor lazygit (or the tool detail's Check Theme Setup) from the
shell used to launch it. This read-only check compares the generated fragment,
reports the captured config selection, exact legacy colon lists and missing
members, and preserves custom overrides. It does not source startup files, parse
personal YAML or inspect a running Git client. JSON output is available with
--json; isolated profiles ignore host LG_CONFIG_FILE.
Fastfetch's managed preset can be checked with slate doctor fastfetch or
Check Theme Setup in its tool page. The check compares the full generated
preset against the saved theme without running Fastfetch or collecting system
information. Missing/unreadable files and differences are reported separately;
personal layouts, shell wrappers, explicit --config options and live colors
are not evaluated. Use --json for a machine-readable report.
Comments and standard JSON whitespace are ignored for preset comparison; literal
values, field order and punctuation still need to match the generated preset.
btop is available in guided tool setup and follows all 20 bundled palettes.
Slate writes one marked asset at $XDG_CONFIG_HOME/btop/themes/slate-sync.theme
(default ~/.config/btop/themes/slate-sync.theme) and changes only the
color_theme value in btop/btop.conf. Other settings and their formatting stay
intact, including layout, update rate and theme_background. This uses btop's
enumerated user-theme mechanism.
Custom btop --config profiles and custom theme-directory precedence are not
automatically discovered. No wrapper or shell startup hook is added.
Reopen btop after applying. An already-running instance may save its older theme
on exit; reapply Slate afterward if needed. The adapter does not send signals or
claim live reload. Quick setup never installs missing btop automatically; guided
installation is optional (Homebrew or the mapped btop apt package, subject to
availability). slate setup --only btop explicitly retries installation; it is
not a preview or a theme-only command.
Theme checkpoints and setup baselines capture both files, including absence and
permissions. slate clean --dry-run previews disconnection: clean resets only
Slate's exact theme reference to btop's Default and removes only the marked asset.
Other themes remain; restoring an older personal theme uses the pre-apply snapshot.
Unmarked same-name assets are not overwritten/deleted. Ambiguous configs, unsafe
links, oversized files and detected concurrent changes fail closed. This is not
an atomic multi-file transaction. All direct adapter tests use disposable profiles.
The Yazi adapter generates UI and code-preview colors for all 20 bundled themes
using the official flavor merge.
It changes only [flavor].dark/light in theme.toml to slate-sync and writes
flavors/slate-sync.yazi/flavor.toml plus tmtheme.xml. Keymaps, plugins, opener
rules and startup scripts are untouched. Other personal theme fields/comments
remain and personal style overrides still win, so live appearance can differ.
Both flavor slots follow Slate's selected theme; no second auto-theme policy is introduced.
slate tools sync yazi --dry-run # review three potential write targets
slate tools sync yazi # explicit confirmation, no software installationPaths use absolute YAZI_CONFIG_HOME or the standard XDG directory; SLATE_HOME
ignores host overrides. Empty overrides are unset; relative overrides are rejected
before writes rather than guessing a destination. Fields target Yazi 26.9.1;
older versions are not guaranteed. Inventory/preview never launch Yazi. Reopen
the file manager after applying; no live reload is claimed. Optional guided
installation uses Homebrew where available. Linux has no unverified apt mapping:
install Yazi manually first. Quick setup does not install missing Yazi automatically.
Sync checkpoints and setup baselines capture all three files, including absence and permissions. Clean preview/execution removes only exact Slate flavor choices and marked assets, keeping other flavors, personal overrides and empty directories. Use the pre-apply snapshot to recover a previous flavor. Unmarked same-name assets, unsafe links, non-regular or over-8-MiB files and detected concurrent edits stop overwrites. Assets publish before selection; multi-file failure can leave partial changes, inspectable/recoverable through the operation's retained checkpoint.
Zellij tabs, pane frames and status components follow all 20 bundled palettes,
using the native component theme format.
Slate writes a marked themes/slate-sync.kdl and updates only the top-level
theme, theme_dark and theme_light string values in config.kdl. All three
follow Slate's saved selection; native automatic hue detection does not choose a
different palette. Personal keybindings, layouts, plugins, comments and formatting
are preserved. This adapter targets Zellij 0.45.1 / KDL v1; older versions are not
guaranteed. No Zellij process or session action is invoked by discovery or sync.
slate tools info zellij
slate tools sync zellij --dry-run # review the two paths
slate tools sync zellij # confirm before writing; does not install ZellijPaths follow Zellij's configuration lookup,
including independent absolute ZELLIJ_CONFIG_DIR / ZELLIJ_CONFIG_FILE overrides
and an absolute theme_dir. A config-file override alone does not move the theme
directory. Without overrides, ~/.config/zellij takes precedence over the platform
directory (macOS Application Support or Linux XDG). SLATE_HOME ignores host
overrides. Empty/relative overrides, implicit /etc/zellij, ambiguous KDL, unsafe
files and same-name personal themes stop writes rather than guessing or overwriting.
The first inspection pins destination paths and their directory aliases for that
Slate invocation; if they change, reopen Slate and review again before syncing.
Other .kdl themes are checked for name collisions (256 directory entries / 8 MiB
total inspection limit). Optional Homebrew installation
uses the existing reviewed installer; no apt mapping or automatic missing-tool
installation in Quick setup is added.
KDL parsing also has a per-document complexity limit of 128: maximum child-block
nesting plus the number of /- directives, including discarded nodes/entries.
Text inside strings, raw strings and line/block comments does not count. Inputs
over this conservative limit are reported before parsing or adapter writes, not
silently simplified. Block-comment interiors are masked only in a private parse
copy at identical byte offsets, preventing the KDL v1 library's recursive comment
handling from overflowing. Original comments, formatting and edited-value spans
are preserved. Parsing uses an in-process worker with a 32-MiB stack reservation;
this does not start Zellij or change the original file.
Native file watching may update running sessions, but Slate does not verify live
appearance. Layout/CLI overrides can still win; start a new session with this
configuration if needed. Sync checkpoints and setup baselines cover both files,
including absence and permissions. Clean changes only exact Slate selections to
default and removes only the marked asset; use the pre-apply snapshot to restore
previous personal choices. Assets publish before config selection; partial failures
retain a recovery point, not an automatic multi-file rollback.
Run slate in a terminal for the menu. Preview Themes / Switch Theme is the
first action; Enter applies the selected look and Esc restores preview changes.
Connect Tools opens a tool menu: sync one available adapter to the saved theme,
inspect availability, or explicitly open guided setup for installation/startup hooks.
Shell Preferences controls the prompt, highlighting and startup banner, not the
adapter catalog. Check Status is read-only. Invalid saved settings are reported,
never displayed as an applied default. Non-interactive entry shows read-only
status and next commands. Interrupted-preview recovery still takes priority.
After a successful action, the main menu returns with refreshed saved settings; canceling a theme preview restores its files and returns here too. Tool pages let you preview potential writes, review/decline a sync, or check supported theme connections without reopening Slate. Use Back to return one level, and Quit / Leave Tools to finish. Confirmed changes are kept when leaving; Back is not undo. Esc/Ctrl+C exits ordinary menus; the theme picker retains its restore-on-cancel behavior. Errors stop the action rather than silently retrying it.
Opening Shell Preferences or the Auto-Theme submenu, then going Back, creates no
profile, sound cache or write lock. Writing begins only after selecting a change;
pairing configuration and guided setup still have their own review flows. Explicit
actions such as slate tools sync btop and slate prompt minimal remain one-shot;
non-interactive inspection does not enter a loop.
The hub's About This Build page shows the running executable's path, embedded source tag, target, Cargo profile class/features and built-in theme/adapter/prompt capabilities. It returns to the main menu without changing settings. For support or when comparing an installed binary with a development build:
slate -V # short package version, unchanged
slate --version # version plus embedded source tag and target/profile
slate about # this executable and its compiled capabilities
slate about --json # read-only report, schema_version 1The direct commands work without HOME, valid settings or Git, including while a
configuration writer or preview recovery blocks mutations. They do not inspect
installed tools, read personal configuration, run native probes or check for
updates. Capability lists come from the compiled registries; they are not proof
that an adapter is installed, configured or visually active. The executable path
can expose your local username; review it before sharing the report. Unavailable
paths/source tags are null in JSON, and non-UTF-8 paths are labeled as lossy.
fnv1a64-v1-… is a non-cryptographic, build-time label over sorted relative paths
and bytes of src/**/*.rs, Cargo manifest/lockfile, the build scripts and selected
embedded resources (listed in build_metadata.rs). Unrelated docs, .git, build
outputs, timestamps and absolute build paths are excluded. Missing, unreadable,
symlinked or oversized inputs yield an unavailable tag, never a partial tag;
scans are limited to 16,384 source-tree entries, 64 directory levels and 64 MiB.
This is not a Git revision, signature, binary checksum or exact-artifact identity:
toolchain, native helper and compiler settings can differ despite equal tags.
Cargo-normalized feature names and Cargo's debug/release profile classification
are separate labels. Packaged/normalized manifests can produce a different tag
from the checkout; no repository is needed when running the compiled binary.
The Tools menu also offers 按用途找工具 (browse by workflow): terminal windows, shell and prompt, files and system, development tools, and split-pane workspaces. For example, the workspace group shows tmux and Zellij together. These are discovery groups, not install/sync bundles; the full supported-tool catalog remains available.
The style page's Check Current Prompt action inspects the captured Starship config selection, saved layout and activation preference without running tools or changing files. It remains available when preferences are unreadable and returns to the same style page. This does not verify the live prompt or source shell files.
Before confirmation, file review also shows any captured STARSHIP_CONFIG
override. This environment hint does not read that file or add it to the write
plan; the listed change targets remain authoritative.
The hub's Prompt Style action offers Rainbow segments, Clean two-line, Compact one-line, Classic shell, Focus one-line and Branch one-line. Themes recolor the layout without switching it back. Minimal, compact, classic, focus and branch presets use ASCII symbols; personal substitutions/icons are retained and may still need your chosen font. Rainbow keeps the plain-font fallback.
Focus shows only the directory and input symbol: green > for success, red x
for failure, with no Git, clock, host display or second line. Review it with
slate prompt focus --dry-run. Personal Git/custom module definitions remain,
but are not included in this layout; switching presets can use them again.
This is not a Git performance benchmark or a switch that disables Git itself.
Branch keeps the directory, Git branch and input on one line: ~/project on main >.
Unlike Compact, it does not include Git change counts; duration and clock modules
are also absent from the layout. Personal module settings remain intact.
Review with slate prompt branch --dry-run or choose Branch one-line in the menu.
Classic is a traditional two-line layout: user, optional @host, directory and
Git context above a literal $ prompt. The dollar is green after success and red
after failure, using the selected theme's palette. Hostname visibility follows
Starship's SSH-only default or your personal visibility/detection rules; aliases,
hostname trimming and other personal settings are preserved. The example shows
an SSH host, not a live connection. Selecting Classic never opens SSH or installs
an icon font, and recoloring/plain-font regeneration keeps the selected layout.
Interactive slate prompt and the hub now let you compare examples before a
theme is saved. Selecting a layout opens an illustration page; it does not prepare
or write personal prompt files. Review File Changes appears for a recognized saved
theme and still asks for default-No consent before applying. Declining returns to
the example, Choose Another Layout returns to the catalog, and Refresh Saved Theme
rechecks a theme changed in another terminal. Unsafe/unreadable theme state never
becomes a guessed default; examples remain available even with broken personal
configs or pending recovery. Actual review/application retains its safety checks.
Choose a Theme First has separate default-No consent: theme preview may temporarily
change detected adapters, and Enter saves theme/opacity across them. It does not
apply the layout being browsed. Saving or canceling the picker returns to that same
layout page; Esc restores preview files. Leaving the layout browser keeps any theme
change already confirmed. Explicit slate prompt <style> remains a one-shot review
and apply command; outside a terminal, bare slate prompt prints the catalog.
slate prompt # browse examples first, then review before applying
slate prompt --list # built-in examples, no profile reads
slate prompt minimal --dry-run # sample and three-file change plan; no writes
slate prompt compact # defaults to No until you confirm
slate prompt classic --dry-run # classic user/host context, no changes
slate prompt rainbow --yes # explicit consent for non-interactive use
slate status --json # prompt_style is the last saved preset, not a live checkExamples are illustrations, not executions of Starship or personal custom
commands. A confirmed change edits the standard XDG starship.toml, the managed
plain-font fallback, and [prompt].style in Slate's config.toml. The preset
replaces the root/right prompt and participating modules' presentation; custom
command definitions, timeouts, detection settings, directory substitutions and
unrelated settings remain. Modules outside the preset are not added to its layout.
No software/font is installed, shell activation is unchanged, and other tools or
the global theme are not reapplied. Existing shell integration is required to see
the result; a custom STARSHIP_CONFIG can override these standard files.
File snapshots precede changes, preserve permissions and include original absence.
Use the reported restore point to get your earlier personal layout back; clean
is not historical layout restoration. Invalid sources, unsafe links and detected
edits since review stop publication. The saved preference is published last;
partial writes remain recoverable, not automatically rolled back. Repeated
identical selections keep file identities and do not create another snapshot.
Theme/font regeneration and missing-config setup seeding honor the saved layout.
--list --json and <style> --dry-run --json are read-only, versioned outputs.
slate tools # tool menu; read-only inventory outside a terminal
slate tools list --json # executable/config evidence, not activation proof
slate tools info yazi # purpose, installation route and next steps; read-only
slate tools info starship --json # versioned detail, even without a saved theme
slate tools install yazi --dry-run # review one missing tool's installation route
slate tools install yazi # separate default-No consent; does not configure themes
slate tools sync btop --dry-run # review potential writes, no native probes or files
slate tools sync btop # defaults to No until you confirm
slate tools sync btop bat --yes # explicit non-interactive consentDetected tools have direct entries in slate tools, even before a theme is saved.
Opening an entry only shows details; without a theme, syncing remains unavailable
and entering theme preview requires a separate confirmation. Back returns to Tools.
Browse Supported Tools is available before setup or theme selection. It lists all 16 adapters, including undetected tools, in an eight-row scrollable menu with no search. A tool page explains its purpose, detected availability, installation route and next steps. Sync actions appear only with a recognized saved theme and a detected tool; this permits a review, not a compatibility or activation claim. Refresh Availability re-reads these prerequisites after changes in another terminal. After synchronization, applied tools receive activation guidance and a read-only doctor/detail command. Partial failures retain guidance for successful adapters, not for skipped or failed ones. This does not verify live colors or regenerate shell startup; full setup remains a separate, reviewed workflow. In tool details, recoverable preview/sync/install/check errors return to refreshed details without automatic retry. Earlier confirmed changes may remain; inspect the result and restore point before retrying. Direct commands still report failures through their exit status, and Esc/Ctrl+C exits the interactive menu. The full Guided Setup wizard requires a separate default-No confirmation from a tool page, since it may change other tools, the theme, font and shell integration. Back from a catalog detail returns to the catalog; merely browsing or declining setup creates no files and runs no tools/installers.
tools info <id> --json exposes schema version 1, tool evidence, purpose, saved
theme/warning, sync_review_available, installation advice, next steps and
recommended_action (action, label, reason and suggested command). Tool menus
select that recommendation initially: refresh unknown availability, review a
missing tool's installation, separately confirm theme preview without a saved theme,
or check existing wiring before syncing. Without a file checker, Preview Sync is
recommended. Opening the page never executes the recommendation.
theme_selection_available distinguishes a missing/unknown theme from unreadable
state. Choose a Theme First opens the existing picker with default-No consent:
previews affect detected adapters, not only this tool. Saving or canceling returns
to the same tool and refreshes its actions without an implicit sync or install.
Unreadable state hides this shortcut and is rechecked after confirmation.
Installation routes reuse the existing setup policy: some adapters are manual
install only, and a route is not proof of helper availability, network or write
permissions. Info remains usable with missing/unknown themes and pending recovery;
it does not inspect application configuration contents or validate live rendering.
Tool details offer Install This Tool when the tool is not detected and the
current setup policy supports an installation route. slate tools install <id>
uses that same review without opening the full wizard. No saved theme is needed.
Already-detected tools are a read-only no-op, even with --yes; this is not an
upgrade/reinstall command. Terminal apps, tmux, Neovim and OpenCode remain manual
installs. Yazi requires the Homebrew route; no apt mapping is guessed.
Use --dry-run (optionally --json, schema version 1) to inspect the requested
tool, action, route, possible Starship fallback and scope notes. Missing tools
require default-No interactive consent or explicit --yes outside a terminal.
Previewing, declining, invalid input and already-detected no-ops create no lock,
profile, snapshot or sound cache, and launch no tools/installers. After consent,
Slate rechecks tool presence and the installation route, runs only exact-tool
platform checks, and acquires its writer guard before calling the shared installer.
Pending recovery stops installation; its read-only preview remains available.
Only one tool is requested, but package managers may change dependencies, caches
and package records outside the Slate profile, including with SLATE_HOME.
There is no package snapshot/automatic rollback or package-version/executable pin.
The reviewed Starship fallback follows existing policy; an uncertain installer
result stops without another automatic attempt. A successful installer exit must
also leave the tool detectable, otherwise the command reports failure and asks
you to inspect installation/PATH before retrying. Slate does not apply colors,
change fonts or activate shell/editor hooks. After installation, the tool page
refreshes; choose a separate sync or setup action when you want configuration.
Sync uses your recognized saved theme, never a fallback. Only selected adapters run: no installs, global theme/pairing changes, shared shell regeneration, or unselected editor notifications. Shell-dependent tools still need existing Slate shell integration; Neovim sync only notifies an existing loader. Missing entry configs need guided setup, not an implicit setup inside sync. Terminal adapters may reapply saved appearance (including Ghostty's font) and reload their windows; bat rebuilds its cache; btop needs reopening. These effects appear in the review.
The preview is a potential-file plan, not a byte diff or a compatibility verdict. Native compatibility checks run only after confirmation. The plan is rechecked under writer exclusion; changed saved themes/destinations require another review. A file recovery checkpoint is required before adapter writes. Partial failure retains successful writes and reports its restore point, without claiming global success. Recovery covers configuration files, not derived caches, running apps, adapter-local backup copies or newly created empty directories. Listing, previews, and declined confirmations create no locks, settings or sound cache.
Check Theme Setup in the tool menu and tool-detail pages opens btop, Starship,
Yazi or Zellij checks, even without a saved theme or a detected executable.
Use slate doctor <tool> with one of those tool IDs and optional --json.
They read files only: no tools/custom prompt commands,
installation, configuration repair or native version probe.
btop checks its standard config's exact asset reference, the generated-file marker
and byte equality with the saved theme's generated asset. Formatting or comments
alone can cause a mismatch; this is not a verdict on live colors. Custom --config,
theme-directory overrides and named-theme precedence are not resolved. A running
instance may save its old selection on exit; close it, sync again, then reopen.
Starship checks captured STARSHIP_CONFIG (otherwise the standard XDG path),
Slate's activation preference, palette entries and saved preset presentation fields.
It distinguishes the plain fallback from the standard profile. Custom overrides
are not edited by slate prompt or selected-tool sync; relative overrides are
reported without guessing a read target. No saved preset means personal layouts
are allowed, not an assumed Rainbow layout.
Yazi checks both flavor slots, separate UI/syntax assets, ownership markers and
exact generated-file matches. Other personal theme.toml sections are flagged as
possible overrides, not removed or evaluated as a complete native merge.
Zellij checks static/dark/light choices separately, resolves the selected profile's
theme directory and checks the generated asset plus same-name inline/directory
conflicts. The conflict scan shares sync's 256-entry / 8-MiB limits; unreadable,
malformed or unsafe inputs are errors, not proof of a clean scan. Custom or missing
slots do not establish Slate selection, while a matching asset alone does not
establish selection or live activation. Unknown saved themes skip color comparisons.
File matches do not prove shell initialization, fonts, native compatibility or
live rendering. Checks remain available during writer/recovery conflicts or broken
backup storage. Bounded reads allow 4 KiB theme records, 256 KiB Slate preferences
and 8 MiB tool files, rejecting final file symlinks, non-regular files and isolated
profile escapes without echoing configuration bodies. Files are observed separately,
not atomically; SLATE_HOME ignores host tool-specific profile overrides. Runtime
layouts, command-line overrides and native version compatibility are not checked.
Live picker previews are temporary: Esc, Ctrl+C or q restores the captured terminal files,
including comments and permissions, and removes files created only for preview.
Pressing Enter also clears the preview before taking the commit's safety snapshot.
Explicitly saved auto-theme choices are kept. Detected external edits or redirected symlinks
are left untouched and reported instead of being overwritten.
Queued keys are handled in order, so quick navigation followed by Enter saves the
requested row. The first confirm/cancel ends input processing. Navigation is batched
into one preview update per changed final selection; Tab, resize, and s save feedback
do not reapply terminal settings. The explicit s auto-theme save remains saved on cancel.
Browse directly with ↑↓ (or j/k), use ←→ for supported terminal opacity, Tab for full preview, and Enter to apply. There is no picker search mode. On terminals supporting bracketed paste, pasted content is ignored as a whole with a brief hint: pasted newlines and shortcut letters cannot apply, cancel, save auto-theme preferences or change the selection. Clipboard contents are never echoed. Cleanup disables the requested paste mode. Unmarked keystrokes cannot be distinguished from normal typing.
Picker frames now use the actual window row/column budget. Family headings count against list space; selection and cancel help take priority over optional mini-preview and opacity chrome. Narrow lines are clipped without wrapping, and the last row has no trailing newline that could scroll the header away. Full preview keeps all eight blocks reachable: PageUp/PageDown scroll with a one-line overlap, Home/End jump to the top/bottom, and a fixed footer shows the visible line range and input help. ↑↓ still selects themes. Scrolling neither applies a theme nor reruns an already cached prompt. Changing themes or toggling Tab resets the scroll position; resizing clamps it, while End stays anchored to the bottom even if the prompt height changes. Long colored prompts retain their styles when paging into the middle; clipping preserves complete numeric SGR (including colon forms) and resets before the footer. Below six list rows or seven full-preview rows, a compact selected-theme view retains cancel help when space permits; paging is inert there. Resizing changes layout, not the selected theme.
Tab's real Starship prompt is optional: its configuration input and generated preview are limited to 8 MiB, and prompt capture has a 750 ms post-spawn deadline with a 64 KiB combined stdout/stderr cap. Invalid configs, failed commands, oversized output and timeouts use the built-in sample prompt; partial output and stderr are not displayed. Non-UTF-8 output also uses the sample. Display filtering preserves readable Unicode, newlines and bounded numeric SGR styles, removes cursor/screen/window/clipboard control sequences and control-string payloads, and resets styling around the prompt block. Hyperlink labels remain text without clickable OSC links; tabs become four spaces and carriage returns are removed. This limits terminal effects, not the content's meaning. Failures are cached per theme until resize or a new picker session. Config checks also reject resolved preview paths escaping the managed directory. These are not a sandbox, an external-editor lock, or a hard time bound on filesystem, spawn and OS termination.
Preview undo records the intended bytes and published permissions of Slate's actual writes, not everything found in a later readback. Parallel terminal adapters explicitly share those receipts; unrelated threads do not. Tracked writes check the captured destination and current contents before opening a temporary file and again before publication. Unrecorded changes detected afterward are preserved for recovery review. These checks are not an atomic lock against an external editor between check and rename.
Cleanup is attempted on normal exit, errors, and the panic hook. Reentrant cleanup while a preview operation is still active stops without changing files or clearing the record. After an incompletely recorded write, only files still matching the last recorded preview writes are automatically restored; unrecorded changes are left for review. Unavailable or inconsistent expected state blocks cleanup instead of disabling its conflict checks. A private, atomically written recovery record also survives a forced process exit. If a preview was interrupted, use the same HOME/XDG environment and run:
slate recover --dry-run # inspect changes; add --json for automation
slate recover # confirm and restore recorded preview changes
slate recover --export ./preview-originals # inspect originals without restoringRecovery refuses an active preview and preserves conflicting external edits.
If interruption occurred before a write was fully recorded, export the originals
for review. To keep the current files and abandon the recovery copy explicitly,
use slate recover --discard (or add --yes for non-interactive confirmation).
recover --dry-run text/JSON output tolerates an early pipe-reader exit, but active,
conflicting or unreadable recovery still fails. Actual recovery, export and discard
require the initial plan to be written successfully before proceeding; this also
applies to the explanation preceding an explicit corrupt-record discard. Output
failure at that point leaves the recovery action unperformed. If an action completes
but its final message cannot be written, the error explicitly says what completed
and that it was not rolled back. Text escapes paths/reasons; the JSON plan is unchanged.
A successful write does not prove the reader reviewed it or make inspection atomic;
existing confirmation and journal validation still apply.
Actual recovery, export and discard now hold the existing cooperative writer lock from plan preparation through confirmation and execution. They recheck the captured record's bytes, identity, permissions and resolved path before acting; detected changes require a fresh inspection. Recovery also rechecks target-file conflicts. Canceling only releases the lock, and dry-run inspection remains read-only. Explicit discard still accepts corrupt private regular records; oversized records are bound by metadata without reading their contents. If the record changes after files have been restored, it is retained and the error says restoration was not rolled back. This is not a filesystem transaction or an atomic lock against external editors.
Restored bytes receive their saved permissions before publication. A failure midway
through recovery can leave some files restored; the error names failed paths and
leaves record finalization unattempted. Reinspect with slate recover --dry-run
before retrying. Finalization errors distinguish a record that could not be removed
from one already removed whose directory sync failed (removal durability unconfirmed).
They report completed restoration, or unchanged configs for discard, without claiming
an automatic rollback. These guarantees do not make multi-file recovery crash-atomic.
The record and exported files are private; JSON previews do not include file contents.
Preview input reads are limited to 8 MiB per file and 16 MiB per collected state;
recovery checks apply the same per-file limit. Binary bytes and captured Unix modes
are preserved, including configurations reached through a resolved dotfile link.
Oversized or unsafe current targets block restoration, but saved originals can still
be exported and the record explicitly discarded. Journals keep their 32 MiB encoded
limit, now enforced during serialization. Encoding failure leaves the previous record
unchanged; this is not a rollback of any preview writes already performed. Failed
initial capture may leave private lock metadata, without changing target configs.
Power-loss recovery is not guaranteed. Live terminal reload is best-effort;
Kitty reloads its original configuration through
load-config.
Slate ships 20 Neovim colorschemes mirroring every terminal family and reloads open buffers the moment you switch.
Works with LazyVim, kickstart.nvim, or a bare init.lua.
Live reload debounces rapid switches and survives atomic state-file replacement
or recreation, provided the state directory remains available. Use
:lua require('slate').stop() to pause syncing, and
:lua require('slate').setup() to read the current theme and restart the watcher.
Repeated setup does not accumulate watchers or exit hooks. Watch failures report
a warning; retry setup after fixing the directory or permissions.
Existing installations need a new slate setup with Neovim integration enabled
to regenerate the loader, followed by a Neovim restart.
slate config set editor disable remembers a profile-local opt-out and removes
Slate-owned activation blocks, keeping colorschemes and the loader for manual use.
Future setup skips Neovim activation, including quick mode. Choosing “show me the
line” or “skip” in setup also remembers manual activation. To allow setup again,
run slate config set editor enable, then slate setup; enabling alone does not
insert a hook. Unmarked user hooks and running editors are unchanged: stop the
watcher or restart Neovim if needed. doctor nvim reports the choice separately
from actual hooks. New snapshots capture it; clean removes it with Slate's loader
directory. Older versions did not record opt-outs, so rerun editor disable once
after upgrading to remember an earlier choice. If hook removal fails, the opt-out
stays saved and the command reports incomplete removal rather than success.
Custom XDG_CONFIG_HOME, XDG_CACHE_HOME, and NVIM_APPNAME paths are supported.
For example, use NVIM_APPNAME=nvim-work slate setup for a separate Neovim profile;
use the same environment when diagnosing or cleaning that profile.
Zsh integration follows exported ZDOTDIR, falling back to ~/.zshrc when unset.
Theme switches reload Slate's colors in the current tmux server without replaying your plugins or other startup commands. Outside tmux, Slate tries the default server; it never starts one just to apply colors. Configuration remains saved if reload is unavailable, and the normal command output explains the warning.
The tmux integration uses the first existing file among ~/.tmux.conf,
$XDG_CONFIG_HOME/tmux/tmux.conf, and ~/.config/tmux/tmux.conf, falling back to
~/.tmux.conf when none exists. Paths containing spaces are supported.
Over SSH, Slate can configure remote tools and reload the remote tmux server.
Terminal fonts, opacity, and graphical terminal reloads belong to the client
machine: configure them by running Slate locally. Preview stays inside the picker.
SLATE_HOME isolation disables live terminal and tmux reloads.
Light mode → your light theme + matching prompt, syntax, tools
Dark mode → your dark theme + matching prompt, syntax, tools
Enable from the hub (slate → Auto-Theme). Every theme family ships a built-in dark/light pair, and you can override the pairing there too.
Official targets: x86_64-apple-darwin, aarch64-apple-darwin, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu. Linux is validated on Debian/Ubuntu + GNOME.
| Tier | Platform | Status & Notes |
|---|---|---|
| Tier 1 — first-class (CI smoke-tested every release) | macOS (Apple Silicon + Intel) | Ghostty, Kitty, Alacritty, Terminal.app (partial — no live preview, no opacity, font cannot auto-apply). |
| Tier 1 — first-class (CI smoke-tested every release) | Debian / Ubuntu + GNOME (x86_64 + aarch64) | Ghostty, Kitty, Alacritty all wired up; live reload works on each. |
| Tier 2 — best effort (wired up, not in CI) | Other Linux distros (Fedora, Arch) and other desktops (KDE, Sway) | Themes still apply; live reload depends on the terminal you run. |
| Tier 3 — out of scope | Windows | No plans to support. |
Shells: zsh, bash, fish. zsh is locally verified; bash and fish are wired up but pending broader testing.
Generated Fish configuration and its setup loader use Fish-specific literal quoting, preserving backslashes and apostrophes in UTF-8 paths and values instead of reusing Bash/Zsh quoting. This covers PATH entries, exported tool configuration paths and wrapper arguments. It does not add support for non-UTF-8 shell paths or make external tool configuration formats accept otherwise unsupported paths.
Generated shell environments keep PATH entries, color/config exports (including
STARSHIP_CONFIG when enabled), and the manual Fastfetch wrapper available to
scripts. Prompt initialization, the minimal prompt, Zsh highlighting, Fastfetch
autorun and the automatic watcher launch run only in interactive shells. Bash/Zsh
check the shell's i flag; Fish uses status is-interactive, following
Bash's interactive-shell check
and Fish's startup-output guidance.
The guards do not exit/return out of the calling script. Sourcing again in an
interactive shell still refreshes features and may run autorun/helpers again;
this change is not a once-per-session cache. Existing managed files gain the
guards on their next successful setup/theme/config regeneration. User-written
startup commands and commands explicitly called by scripts are unaffected.
bat configuration and cache rebuilds
Slate uses the detected bat or batcat executable, including home-local fallback
locations. The config file (BAT_CONFIG_PATH), custom assets (BAT_CONFIG_DIR),
and compiled cache (BAT_CACHE_PATH) are separate paths; a config-file override
does not relocate themes/. Defaults are $XDG_CONFIG_HOME/bat and
$XDG_CACHE_HOME/bat. This follows bat's directory selection.
Paths are captured once per operation; relative overrides are anchored to the
starting working directory. An explicitly empty override selects that directory,
not the XDG default. SLATE_HOME and injected test profiles ignore ambient bat
overrides. Theme apply never edits the bat config file itself.
Cache rebuilds have a 30-second post-spawn deadline and a combined 256 KiB output cap. Failed exits, timeouts, output overflow and bat's known “built without build-assets” response fail the apply, even when that response exits zero. The last committed Slate theme remains unchanged on failure; generated theme files and partially changed external caches are not automatically rolled back. Native output is omitted from errors. Fix the underlying bat/cache issue and retry. The executable is not sandboxed; OS calls and detached processes are outside the deadline guarantee. Successful exit is not a native rendering test.
Per-terminal status
| Terminal | Status | Notes |
|---|---|---|
| Ghostty | Recommended | Full support — live reload, opacity, watcher relaunch |
| Kitty | Full | Live palette push via kitten @ set-colors; opacity + Nerd Font sync |
| Alacritty | Full | Inline preview and reload |
| Terminal.app | Partial | macOS only — no live preview, no opacity, font cannot be auto-applied |
| Other | Best effort | Shell and CLI tool theming works; terminal visuals depend on the app |
All commands
slate # interactive hub
slate setup # guided setup
slate setup --quick # non-interactive, defaults
slate setup --only starship # retry a single tool
slate theme # live preview picker
slate theme <name> # apply by name
slate theme --auto # follow system dark/light
slate font # Nerd Font picker
slate font --list # read-only candidate list and download catalog
slate font --list --json # versioned font inventory, including partial-scan evidence
slate font --list --search "mono jetbrains" # filter families and catalog IDs
slate font jetbrains-mono --dry-run # preview file changes; no download or write
slate config set opacity frosted # solid / frosted / clear
slate config set sound off # toggle feedback sound
slate config get sound # read one preference, without changing files
slate config list --json # list resolved preferences, defaults and read errors
slate config pairing --json # inspect saved dark/light pairing without desktop queries
slate config pairing --dark nord --light catppuccin-latte --dry-run # preview only
slate config pairing --dark nord --light catppuccin-latte # save pairing only
slate config pairing --clear-dark --clear-light --dry-run # preview removing both overrides
slate export # export current config as URI
slate export --raw # one plain share-code line for scripts
slate import <uri> # re-apply from URI
slate import <uri> --dry-run # inspect requested settings without applying
slate import <uri> --dry-run --json # versioned, profile-independent preview
slate share # screenshot terminal with watermark
slate about # identify this binary and its built-in capabilities
slate about --json # read-only build/capability report, no profile required
slate status # show current config
slate status --json # read-only saved settings and preview recovery summary
slate doctor ghostty # inspect Ghostty config issues
slate doctor ghostty --files-only --json # inspect references without launching Ghostty
slate doctor kitty # inspect theme includes and live-reload settings
slate doctor alacritty # inspect TOML and theme imports
slate doctor opencode --json # inspect Slate's selected TUI file and theme setting
slate doctor opacity --json # compare saved opacity with generated files, without writes
slate doctor font --json # inspect saved family, candidate scan and generated font files
slate doctor nvim --json # inspect the active Neovim profile as JSON
slate doctor nvim --check-version --json # explicitly run a bounded version check too
slate doctor zsh # inspect the active .zshrc and Slate loader
slate doctor bash --json # inspect the startup file selected by Bash setup
slate doctor fish # inspect Slate's conf.d loader and managed environment
slate doctor auto-theme --json # read-only watcher, launcher and conditional theme choices
slate list # list available themes
slate list "rose dawn" # search IDs, display names and families
slate list --appearance light # keep only light themes
slate list catp --json # versioned, read-only catalog for scripts
slate list --ids # canonical IDs only, one per line
slate completions zsh # print static completions (also bash or fish)
slate restore # pick a snapshot to roll back
slate restore --list # list restore points
slate restore --list --all # include undo checkpoints
slate restore --list --json # read-only history summary and issues
slate restore <id> --dry-run # preview snapshot changes without restoring
slate restore <id> --dry-run --json # machine-readable restore preview
slate clean --dry-run # inspect cleanup without writing files or stopping tools
slate clean --dry-run --json # machine-readable cleanup plan
slate clean # back up, then remove Slate hooks and managed filesslate config pairing [--json] inspects saved dark/light slots. It does not resolve
the current system appearance or infer a saved value for an unset slot. Add
--dark <ID> and/or --light <ID> to save exact catalog IDs of the matching
appearance; the unspecified slot, unrelated TOML fields, comments and permissions
are preserved. slate list --appearance dark|light --ids lists choices, and static
completion filters each option accordingly. Add --dry-run to preview selected
changes without writes or a lock. Reads/previews remain available during another
writer or pending preview recovery; real saves are blocked in those states.
Use --clear-dark and/or --clear-light to remove saved overrides and return those
slots to normal automatic fallback. You can clear one slot while setting the other;
setting and clearing the same slot is rejected. Clear supports the same --dry-run
and JSON receipts. It does not disable the watcher, change the active theme or force
brand defaults: fallback may still use the current theme or its catalog pair.
Inspect slate config pairing afterwards to see the conditional choices.
Only selected keys are removed; the file itself is retained, even if it becomes
empty. Clearing an already-unset slot creates neither a new preference file nor a
restore point (ordinary writer-lock setup may still occur). Existing unrelated data
and permissions are preserved; comments attached to removed assignments are kept as
standalone footer comments, so their positions may move. Unknown string IDs can be
cleared, but malformed TOML or non-string pairing fields must first be corrected.
Inspection also explains the automatic choice for each possible appearance,
without querying the desktop. Additive JSON v1 resolution.dark/resolution.light
entries have resolved/error status, requested and selected appearance, a known
theme ID and source: configured, current_theme, catalog_pair or
brand_default. Defaults explain missing/unknown current tracking or a missing
catalog pair through fallback_reason. Saved-slot unset remains separate from
these conditional choices; previews and save receipts do not include resolution.
This is selection guidance, not a check that target files can be applied or that a
running watcher is healthy. Pairing and current tracking are separate observations,
not an atomic configuration snapshot.
The same selection policy is used by theme --auto and the watcher. Known stored
overrides and catalog self-pairs (such as Nord) remain authoritative even when the
selected theme has a different appearance; the report exposes that difference.
Unknown selected pairing IDs fail rather than silently falling back or echoing
their contents. Automatic selection now uses the same strict regular-file read
contract as inspection: 256 KiB for auto.toml, and 4 KiB for current only when
a fallback needs it. Final links, special files and isolated-profile escapes are
rejected; safe directory aliases remain supported. An unreadable current file
does not hide an explicit choice that does not depend on it. Actual application
still validates its write targets separately.
Saving changes only auto.toml, after a pre-config checkpoint; identical repeats
do not rewrite it or create another checkpoint. Invalid documents, unsafe paths or
backup failures stop before publication. Reads are bounded to 256 KiB and late file
or resolved-parent changes are checked before writing; external editors are not
locked out. JSON v1 uses inspect, preview, saved or unchanged actions,
per-slot set/unset/error statuses, before/after selections for changes and a
restore ID when a checkpoint was created. Unknown saved IDs and document errors
are reported without echoing their contents. Inspection issues still exit zero;
invalid requests or failed previews/saves exit nonzero.
The interactive config set auto-theme configure menu and hub now use this same
file-only save policy. Declining confirmation does not save, refresh shell files
or restart the watcher. Saving pairing no longer restarts the watcher or applies
a theme immediately, and does not change the enable flag. An existing watcher
reads the new pairing on the next appearance event; run slate theme --auto to
apply now. The interactive menu requires a terminal; scripts should use the new
options. File recovery does not restore processes or automatically apply a theme.
slate config set fastfetch enable|disable and slate config set auto-theme enable|disable
prepare generated shell files before changing the preference. A pre-config
checkpoint captures the affected files (including watcher helpers for auto-theme);
backup failure stops the update. Generated files are published first, the preference
last, preserving existing permissions and unrelated TOML values and comments.
Repeating an identical Fastfetch setting skips file writes and a new checkpoint;
auto-theme can still repair helpers or retry its lifecycle.
Errors distinguish helper preparation, file updates and watcher start/stop. If the
lifecycle fails after saving, the new preference stays saved; Slate does not reset
it silently. Inspect slate doctor auto-theme and the printed
slate restore <id> --dry-run before file recovery. Checkpoints restore files only,
not processes. These stages are not an all-files/crash-atomic transaction: late
failures may retain earlier writes. Captured inputs and upcoming output files are
rechecked, but external editors are not locked out. This applies to these enable/
disable commands, not the separate auto-theme pairing/configure flow or every
configuration setter.
slate config get <key> [--json] reads one preference; slate config list [--json]
reads all five public keys without creating files, taking a writer lock, starting
tools or initializing sound. Values are resolved preferences, including defaults,
not a claim about live terminals/editors/watchers. Missing opacity is unset, not
an inferred preset. With no stored settings, auto-theme/Fastfetch are disabled and
sound/editor setup permission enabled. editor describes future setup consent,
not an installed hook; auto-theme is its enable flag, not the configured pairing.
JSON v1 entries include key, typed value, status (ok, unset, error),
meaning, source path and set_values (accepted set actions, not stored-value types).
Errors have a null value and do not hide valid preferences. Inspect entry statuses:
like doctor, an emitted report exits successfully even when it contains issues.
Invalid command arguments and output I/O failures still fail; an early pipe reader
exit is normal. Reads remain available during a writer or pending preview recovery.
Inspection refuses final links, special files and isolated-profile escapes; ordinary safe parent aliases remain supported. Documents are bounded to 256 KiB, state files to 4 KiB, and configuration contents are omitted from errors. Reads are independent, not an atomic snapshot. Fastfetch's shared marker getter also rejects unsafe files and state over 4 KiB, rather than treating a directory as enabled or a broken link as disabled; bounded ordinary payloads retain presence semantics. Invalid set/get keys and set actions are rejected before profile or lock setup, with bounded, display-escaped messages. Static completion includes get/list and get/set keys.
Auto-theme doctor and config get/list share the strict preference reader and TOML
field validation. Broken parent links and isolated-profile escapes report unknown/error,
not a disabled default, even when the final file appears missing. Valid parent aliases
inside the profile still work. Auto-theme and sound inspections reject final file links
at open time as well as during path validation; ordinary runtime getters retain their
existing linked-dotfile compatibility. Neither diagnostic initializes configuration,
sound or a watcher, and separate reads are not an atomic snapshot.
Font copies from Caskroom recovery or downloaded releases are staged as a complete
family before installation. Identical existing files are kept; differing files or
unsafe links stop the copy without replacement. Mid-batch failure rolls back this
operation's unchanged additions; externally changed files are retained with review
paths. Direct downloads use HTTPS-only redirects and bounded curl execution, with
implicit curl configuration disabled. Rust checks ZIP/ZIP64 paths, types, sizes
and font CRCs before publication; system unzip is no longer required. These checks
do not authenticate the upstream publisher, cover Homebrew's own installation or
guarantee native font activation.
Linux cache refresh is a separate, bounded step. Missing fc-cache, timeouts or
refresh failures produce a warning while keeping installed fonts; they do not
trigger another download. Setup and the font picker report the actual outcome.
Selecting an already-installed font does not run or claim a new cache refresh.
On Linux, user-font discovery, file installation and cache refresh share
$XDG_DATA_HOME/fonts, defaulting to ~/.local/share/fonts when the variable is
unset, empty or relative. Absolute custom data roots can live outside HOME;
explicit root aliases are resolved, while a linked fonts child stops writes
and refresh. Installation also rejects parent traversal in the configured root.
Old fonts are not moved or removed, and the old default directory is not treated
as active when an override is selected. Legacy ~/.fonts and the existing system
search roots remain. macOS still uses ~/Library/Fonts; SLATE_HOME isolation
ignores the external override. Arbitrary native Fontconfig search configuration
and XDG_DATA_DIRS are not interpreted by Slate.
Font discovery now includes nested directories, normal linked fonts and
case-insensitive .ttf, .otf, .ttc and .otc names. A regular-file and small
header check filters out directories, empty files and obvious non-fonts. If a scan
is incomplete, known candidates stay selectable but missing-font downloads are
held until the scan issue is resolved. Candidate names still come from filenames;
this is not OS registration, internal family-name or glyph-coverage verification.
Use slate font --list [--json] to inspect candidates without opening a picker,
reading saved settings or acquiring a writer lock. It stays available during
pending recovery and reports partial scans with known candidates intact. Catalog
entries are separate: candidate_found, not_observed or unknown; unknown
entries are not offered for download by the picker. These are filename-derived
observations, not native installation or glyph-coverage claims. JSON includes
exact candidate family strings, catalog IDs and matching names, search roots,
scan issues and lossiness flags. A successful exit means report production;
check scan_complete before inferring absence. Listing never selects, installs
or refreshes anything. --json requires --list or --dry-run, and a font name cannot accompany
--list. Choose later with slate font -- '<exact family>'; a catalog choice may
install fonts. Static shell completions include the inspection flags.
Use slate font --list --search "mono jetbrains" [--json] to narrow the view.
Search ANDs case-insensitive alphanumeric terms across family names and catalog
IDs; punctuation separates terms and their order does not matter. It is substring
search, not fuzzy selection. Empty/whitespace searches show everything, while
symbol-only queries match nothing; use --search=--- for leading-dash values.
Queries are limited to 256 bytes. The full scan still runs: search does not alter
scan issues, catalog presence, matching-family evidence or download eligibility.
No matches means only that the query matched no displayed choices, not that a
font is absent. Schema-v1 JSON adds search only when supplied, with the original
query, total/matched candidate counts and total/matched catalog-entry counts.
The picker shares this model, retains all JetBrainsMono variants and never derives
the selected family from display decorations. Literal names ending in
(recommended) or (not installed) are kept as data, not stripped as UI badges.
After a complete scan, an unknown name offers up to three nearby exact-family
hints, labeled as observed candidates or catalog choices that may download.
Ambiguous aliases list matching exact names instead of choosing one. Hints are
advisory and display-escaped, never shell commands or automatic selections;
inspect your chosen exact name with --dry-run. The same guidance appears in
the preview's blocker.reason; incomplete scans retain their existing block.
Suggestions score at most 4096 unique observed names in sorted order and disclose
any truncation; --list retains the full captured inventory.
Preview a named selection with slate font jetbrains-mono --dry-run [--json].
Unlike listing, this reads your current configuration and uses the same resolver
and prepared transforms as application. It lists ordered file actions (create,
update, unchanged, preserve_absent) and byte counts without exposing file
contents, plus whether catalog installation, a pre-font checkpoint or a
session-eligible Ghostty reload would be requested. --dry-run requires a name
and cannot be combined with --list; it never opens the picker.
A produced preview exits successfully even when blocked: check
file_plan_complete and blocker in schema-v1 JSON. Only the first observed
blocker is reported; incomplete plans contain no file actions. Known candidates
remain usable during partial scans, but unknown catalog selections stay blocked.
Preview acquires no lock, creates no backup, writes nothing and launches no tool;
it is available during writer contention or pending recovery. It does not
test those mutation gates, write permissions, backup creation, network access or
native rendering (execution_readiness_checked: false). A complete projection
is not a reservation or a guarantee that later application will succeed.
Font selection now prepares terminal and Shell output before writing. A changed
selection creates a pre-font file-recovery point before any catalog download;
an unreadable/unsafe target, invalid Alacritty document or failed checkpoint stops
application. Limits are 8 MiB per terminal/Shell file, 256 KiB for preferences and
4 KiB for tracked state. Writable final symlinks and conflicting target aliases
are rejected; absent optional terminal entry files stay absent. current-font
is published last, and session-eligible Ghostty reload is requested only after
all writes succeed. Unchanged files retain bytes, modes and identity; an identical
repeat needs no new checkpoint. Imports reuse their wider pre-import point.
Explicit names and interactive selections use the same application flow: a
failed download exits with an error and does not begin font configuration writes.
Installer font-file/cache changes can still remain. --quiet hides font success
receipts and download progress, not the picker prompt, errors, cache warnings or
recovery guidance on stderr. --auto suppresses the new-shell reminder and sound;
ordinary successful selections show the reminder in both modes. Basic Starship
selection is based on the family name, not verified glyph coverage.
Homebrew font installation has a 10-minute wait limit after process startup and a 512 KiB combined stdout/stderr limit. Timeout, excessive output, signal termination or an unconfirmed capture stops automatic shared-cache/download fallbacks. Inspect Homebrew before retrying: font files, package records or caches may already have changed. Ordinary completed failures keep the existing fallback order. Setup and direct/picker/import selection now share that policy: all-path failures retain each attempted stage's reason, including shared-cache failures. Recovered failures remain notices (also visible in quiet font selection), and setup reports the successful source. A cache-refresh warning after successful font publication does not trigger another installer or download. Native Homebrew output is classified but not echoed. These are not filesystem/spawn/ OS-termination deadlines.
Homebrew tool installs share this execution path with separate limits: 30 minutes after startup and 2 MiB combined output. An unconfirmed tool install stops the remaining setup installers and configuration steps, including any Starship local fallback. Setup reports its existing file-recovery checkpoint; earlier installs/configuration are not automatically undone. Ordinary completed permission failures retain the existing Starship fallback policy. These changes do not sandbox Homebrew or detached subprocesses.
Linux apt tool installs now use the same bounded capture mechanism: 30 minutes
after startup and 2 MiB combined output. Effective root invokes apt-get directly;
other users use sudo -n without a password prompt in Slate. If authentication is
needed, authenticate in your own terminal and retry Slate there as your normal
user, or ask an administrator to install the package. Slate does not weaken sudo
policy or retry through a privileged shell. Debconf uses noninteractive defaults;
package scripts/conffile decisions can still fail and need manual attention.
--no-remove refuses transactions requiring package removal. No automatic
repository update, lock deletion or dpkg repair is performed. Normal failed exits
report an issue and may leave partial changes; setup can continue independent
steps. Unconfirmed capture, timeout, output overflow or signal termination stops
remaining setup. Elevated/detached installers may still be running: inspect
apt/dpkg before retrying, without deleting lock files. These are post-spawn wait
limits, not OS termination deadlines or package rollback. Errors classify bounded
native output without echoing it. Package availability depends on configured
repositories. On the apt backend, Starship directly uses the staged user-local
installer below, without first attempting sudo/apt.
The user-local Starship fallback now stages the official installer in a private
temporary directory. Fetching the script uses HTTPS-only redirects, disables curl
configuration and allows 75 seconds after startup / 1 MiB combined output; the
installer allows 10 minutes / 2 MiB. Slate validates a nonempty, owner-executable,
regular non-symlink binary (at most 64 MiB), then atomically replaces
~/.local/bin/starship with mode 0755. HOME aliases are resolved, but linked or
non-directory .local/bin components and linked/unsafe targets are refused
before download. Target identity/content is rechecked before publication so an
observed external change is not overwritten. Unconfirmed installer execution or
publication stops the remaining setup. No binary is executed for validation.
This still trusts the upstream script: staging is not a sandbox, an extraction
disk quota, authenticity/runtime verification or exclusion of external writers.
Pre-publication failures do not make Slate replace the old binary, but arbitrary
installer side effects and newly created empty directories can remain. File
recovery does not uninstall or restore this executable.
Use the printed slate restore <id> --dry-run to inspect recovery, then explicitly
restore if needed. A late failure can leave earlier writes in place: this is not
automatic multi-file rollback, crash atomicity or exclusion of external editors.
Recovery covers captured configuration bytes, modes and absence, not installed
fonts, external caches, empty directories or live windows. A successful config
write/reload request is not proof of native font matching or rendering.
If a selected font is not appearing, run slate doctor font [--json]. It reports
the saved family, selected user-font directory, all search roots and bounded scan
issues, then compares Ghostty, Alacritty and Kitty font outputs against the same
serializers used by their writers. Missing files can be normal for unused
integrations; differing bytes are not a syntax or runtime judgment. A positive
candidate remains reportable during an incomplete scan; a negative result never
proves a font is uninstalled, because discovery uses filenames and a limited
system-family whitelist. Review include chains, overrides and native font matching
separately. No terminal, cache builder, installer or writer is launched, including
while a write lock or recovery record is present. Valid saved family names are
shown; invalid state and generated file contents are omitted. JSON adds a bounded
font_inventory summary to the versioned diagnostic report. Exit success means a
report was produced, not that every check passed.
The same diagnostic now also checks direct terminal entry references using the
font receipt's shared bounded reader/parser. Schema-v1 JSON adds font_references:
one summary for Ghostty, Alacritty and Kitty, each with its managed target, selected
candidate entries, state, inspection_complete and loss-aware paths. States are
found, not_found, missing and uninspectable. A known positive entry remains
found if another entry is unreadable, with inspection_complete: false and the
individual warning retained. Missing optional entries can simply be unused tools.
Generated bytes and direct references are separate evidence, not activation proof.
Entry checks follow regular file links read-only, reject targets outside isolated
SLATE_HOME, and cap each UTF-8 file at 8 MiB. Invalid TOML, NUL/binary contents,
unsafe files and Ghostty's pinned line/reference limits are unknown, not missing
references. Ordinary profiles may inspect configured external entry links; accepting
a link for diagnosis does not authorize replacing it during an application. These
checks never traverse includes or launch native validation. Relative/variable or
nested references, cross-file resets, overrides and actual rendering remain outside
this check; inspect the per-entry evidence before deciding to reapply or reinstall.
slate doctor ghostty [--json] retains candidate selection, duplicate managed
references and literal include-cycle detection. Its scan now reads regular UTF-8
files with limits of 8 MiB/file, 32 MiB total, 256 paths, 4096 references and 64
include levels. Ordinary symlinked configs remain supported; references resolving
outside SLATE_HOME are reported without reading their contents. FIFOs, dangling
links, unreadable/oversized files and scan limits produce scan_issues and
scan_complete: false, not a claim that no cycle exists. Missing files alone do
not imply a cycle. JSON retains the existing fields and adds schema_version: 1,
scope and path-lossiness indicators; cycle_risk describes only observed findings.
Entry candidates follow the pinned Ghostty 1.3.1 default-file order:
XDG config, then config.ghostty; on macOS, App Support config, then
config.ghostty. Slate writes managed references to the last existing candidate,
and still uses XDG config.ghostty when none exists, including on macOS. The
adapter, font/config detection, doctor labels and backup keys share this path list;
existing backup keys keep their original meaning. JSON's entry_order names the
pinned ordering baseline. This is Slate's write-target policy, not an installed
version probe or a simulation of native preferred-file checks, command-line
overrides, recursive load order or window state. In particular, native macOS
preferred-file checks can skip an empty or unreadable current App Support file.
Ghostty window layout remains your choice. Slate updates the palette and
window-theme light/dark appearance, but no longer writes macos-titlebar-style.
Choose that option in your own Ghostty config or an included file, not Slate's
regenerated managed/ghostty/theme.conf. Applying a theme with this version removes
the older forced transparent line from that generated file. With no explicit
choice, Ghostty uses its own default (transparent in 1.3.1).
slate doctor ghostty now points out macos-titlebar-style assignments still
present in a referenced, regular managed/ghostty/theme.conf, with the file and
first assignment line. Schema-v1 JSON adds window_style: managed_override
means an assignment was observed, not_found means none was found in a complete
scan, and unknown means an incomplete scan found none. Positive findings survive
other scan failures with inspection_complete: false. This advisory neither
makes the reference scan incomplete nor prevents native syntax validation.
Values are omitted from the advisory; user-owned settings, lookalike paths,
unreferenced files and targets of managed-file symlinks are not attributed to
Slate. Inspection uses the same bounded include scan and does not change files.
Reapply your desired theme with this version to regenerate an old managed file;
the diagnostic does not claim which style is effective or whether tabs look right.
If the macOS tab strip looks gray against a colored terminal, a successful theme
apply does not prove that native tabs match the palette. transparent retains
native controls; tabs integrates tabs into the titlebar, changing the layout.
Compare in a new window after reloading: Ghostty applies titlebar-style changes
only to new windows. System tab materials and selected/unselected contrast still
need visual checking; Slate does not recolor native widgets or automatically
reopen windows. See Ghostty's titlebar options.
Reference parsing follows Ghostty 1.3.1's
line reader
and path parser,
not Shell syntax. Unquoted spaces, #, =, single quotes and backslashes remain
literal path data; surrounding double quotes are handled by both upstream stages.
UTF-8 BOMs are accepted. Optional ? references may be absent; missing required
references produce required_file_missing, even after an optional reference to
the same missing file. Optional does not waive safe-read checks. Absolute and ~/
file links retain their nested-relative base; existing relative links resolve to
the real file first. JSON's reference_syntax names this reference-grammar baseline,
not the installed binary's version or the adapter's entry-selection policy.
List resets discard earlier local references but report reference_reset, because
cross-file reset order is not modeled. A line exceeding the upstream reader's
4094-byte input capacity produces line_limit; subsequent lines are not scanned.
These limitations are explicit incomplete scans, not evidence of invalid native syntax.
Ghostty font attachment and Slate-reference cleanup now share this literal
grammar with diagnostics. A path embedded in an unrelated value is not a managed
reference; single-quoted relative values and .. ownership ambiguities are kept.
Font attachment checks references after the last local list reset and does not
duplicate a valid later reference merely because an earlier legacy hint exists.
Cleanup preserves other bytes and the leading UTF-8 BOM. These byte transforms
do not expand arbitrary paths or perform whole-file/native validation.
Font receipts report observed direct references, not effective activation. Ghostty uses complete literal values and local resets; Kitty uses its logical continuation lines and complete include values; Alacritty retains effective import precedence. An uninspectable entry is distinguished from an absent/unlinked file. The receipt does not traverse include chains or verify cross-file resets, overrides, native font matching or window rendering. Use the relevant doctor for further checks.
On a complete, non-isolated profile it still runs the selected Ghostty's
+validate-config, with closed stdin, a five-second post-spawn deadline and a
64 KiB combined stdout/stderr capture limit. Timeout or excess output terminates
only that invocation's process group and reaps its leader, including when an
inherited pipe remains open. Validation distinguishes passed, failed, skipped,
timed_out, output_limit and error; JSON exposes the limits and truncation flag.
Text escapes terminal controls and shows at most eight nonempty native output lines;
native output may contain configuration values, so review before sharing. A complete
report exits zero; scripts must check both scan completeness and validation status.
This is a best-effort file scan, not a full Ghostty parser or a live-window check.
Slate does not edit settings, but native validation runs Ghostty itself. Isolated
or incomplete scans do not launch it. Filesystem IO/OS process teardown have no
hard wall-clock guarantee, and external edits are not locked out.
Font application and theme reapplication use terminal-specific serialization:
Alacritty gets valid TOML, Ghostty keeps its literal quoted values, and ambiguous
Kitty names use the explicit family= syntax (Kitty 0.36+). Ordinary Kitty family
names retain their legacy format. Control characters, line separators and names
over 256 bytes are rejected. Exact installed family names take priority over loose
aliases; ambiguous aliases require an exact choice instead of silently selecting
another family. Unset fonts fall back only to discovered installed families, not
the picker's “not installed” recommendation label.
Setup's initial font/theme hints are read-only and use the supplied profile, including custom XDG directories. Font hints inspect direct Ghostty entries first, then the adapter-selected Alacritty TOML file (also accepting inline/dotted font tables); imports and live window settings are not inspected. Reads require regular files, with 8 MiB per terminal config and 4 KiB for the saved theme. Unsafe or unreadable sources provide no hint; font names must pass the same validation as writers, and control-bearing theme IDs are omitted. Ordinary dotfile links remain supported, but isolated profiles do not read linked sources outside their resolved home. This does not repair invalid settings, approve them for later writes, impose a filesystem IO deadline or lock out external directory edits. Startup inspection alone creates no directories and launches no tools.
The wizard retains that profile for tool selection and uses its captured theme and
session for hints/review rendering; it does not reread another profile for those
screens. slate setup --only <tool> retries installation only, without reapplying
themes or shell integration. It keeps the supplied profile for installation and
returns failure when preflight or installation fails. Exact-tool retry checks only
OS, architecture and the selected installation route; it does not scan unrelated
fonts/tools, probe DNS, check shell integration or create a configuration write
probe. The normal write guard and installer-specific checks still apply.
Unknown/detect-only targets are rejected before profile/lock/sound initialization.
For full setup, the write-permission probe creates and removes its own random temporary file in the configured XDG
directory; it never reuses .slate_preflight_test, and individual write targets
still need later checks. A profile is not a package-manager sandbox: executable
discovery and system package installations still use the host's existing backend.
On macOS/Linux without Homebrew/apt, slate setup --only starship can use the staged
user-local installer directly. Guided setup no longer requires a package manager
before you choose anything: its inventory labels manual-install-only tools, offers
only supported automatic-install routes, and preserves configuration choices for
already-detected tools. Standalone font-download intent does not itself require a
package manager. Quick derives its core tools from the supported shell named by
SHELL: Starship for Bash/Fish, plus zsh-syntax-highlighting for Zsh. Bash/Fish
do not select the Zsh-only plugin for installation or theme configuration, so its absence
does not require a package manager or imply a download. Existing Zsh files and
preferences are not removed; Manual and --only zsh-syntax-highlighting retain
explicit installation support. Quick still requires routes for every missing
core tool relevant to that shell; it will not silently drop unavailable tools.
This also applies to Quick chosen inside the wizard. Final selections are checked
again before snapshots, preferences or installation; --force does not bypass
route validation. A supported route does not prove curl/network access, writable
targets, package availability or runtime success: installers check their own
dependencies. Network preflight reports DNS evidence, not verified download access.
The confirmation card now shows each tool's actual route: Homebrew formula/cask and package name, the mapped apt package and administrator access, or the full user-local executable destination. Starship's known-failure Homebrew fallback is shown before confirmation, together with the configuration-only backup scope. Those tool actions are retained in the execution plan. Changed selections, package metadata, local destination or routes require a new review before the snapshot; routes are checked again before execution and before each tool starts. Execution consumes the captured route rather than choosing another backend. A later change stops remaining setup and retains existing recovery guidance; earlier completed/partial changes are not undone. This does not pin package versions, executable identities or repositories, freeze font installation paths, exclude external changes or sandbox installers. The disclosed Starship fallback still applies to known Homebrew failures; unconfirmed installer outcomes stop.
Full setup also returns a failure status if a requested installation, font choice, theme/shell stage or Neovim activation step fails; a partial run no longer emits the whole-setup completion event. Optional notices and deliberately skipping Neovim's activation line do not fail setup. Font availability and a saved font choice are reported separately, neither proving live rendering. SSH/isolated receipts do not claim local window activation. Fastfetch/opacity preference-write errors stop the run before installation. Neovim marker inspection accepts regular files up to 8 MiB (including non-UTF-8 surrounding bytes), and reports unsafe/unreadable sources instead of silently treating them as an absent installation.
If setup fails after its safety snapshot, the error includes
slate restore <id> --dry-run for inspecting captured file recovery. Earlier
successful changes remain in place; there is no automatic rollback. File recovery
does not uninstall packages or fonts or restore live windows.
After confirmation, setup prepares a read-only execution plan before its safety snapshot, preference writes or installation. Unknown installation/configuration targets, detect-only installation requests, malformed font names, invalid themes and unsupported shells are rejected early; saved themes are checked when keeping the current selection. Repeated tool IDs run once in their original order; known font display names resolve to their catalog IDs. Execution keeps the planned profile, theme and shell instead of resolving them again. Successful shell-only setup also saves its chosen theme after connecting the loader. Planning does not verify installed fonts, network access or all write targets, and does not prevent external filesystem changes during execution.
Setup also prepares the selected shell's loader before its safety snapshot or
installers. It accepts regular files up to 8 MiB, rejects final symlinks (including
dangling links), unsafe parents and malformed Bash/Zsh marker blocks, and checks
the generated size. Execution rechecks the captured bytes, ordinary permissions,
file identity and resolved parent path before installing tools, before theme/shell
work and before loader publication. Detected changes require a fresh setup run.
Loader updates use atomic replacement, preserve existing ordinary permissions,
create new entries with mode 0600, and leave identical files untouched. Bash/Zsh
preserve non-managed bytes; Fish's Slate-owned conf.d/slate.fish remains a whole
generated file (not config.fish). This is not a setup-wide transaction: later
write failures can leave earlier tool/theme changes and created directories in
place, with the existing file-recovery guidance. Parent-directory synchronization
is best effort, and external writers are not excluded.
slate doctor bash|zsh|fish [--json] uses the same startup-file selection as setup
and checks its managed env.<shell> separately. macOS setup targets login Bash:
it selects the first present .bash_profile, .bash_login or .profile, in that
order, and creates .bash_profile only if no login entry exists. This prevents a
new file from shadowing an existing profile under
Bash's startup rules.
Unlike Bash's runtime readable-file search, Slate deliberately stops on an unsafe
or unreadable higher-priority candidate rather than writing a different entry.
Linux continues to target .bashrc. Existing .bashrc files are left intact on
macOS; setup does not add login/non-login chaining or migrate old loader blocks.
The shared .profile gets a Bash-version guard around only Slate's source block
so other shells can keep using its user content. Snapshots and clean/restore cover
all four Bash entries; cleanup removes only the managed marker block. Setup
rechecks both the selected entry and managed environment path before writes, so a
new higher-priority file requires a fresh run. This is not concurrent-writer exclusion.
Doctor reports the platform's convention and creates no files. Zsh honors the
selected ZDOTDIR; Fish checks only Slate's conf.d/slate.fish, not config.fish.
The checks report missing/unreadable/oversized files, final links, special files,
isolated-profile escapes and malformed Bash/Zsh markers. Files are read separately,
up to 8 MiB each, without interpreting or echoing source bytes. Shell diagnostics
permit opaque non-UTF-8 file contents; non-UTF-8/multiline paths make literal-reference
comparison unavailable. No shell or installer is launched and no configuration is
repaired, even while a configuration writer is active.
A matching literal source line (or . for Bash/Zsh) is only textual evidence,
not proof it executes: startup precedence, shell syntax, control flow, functions,
heredocs, multiline quotes and indirect/dynamic sources are not evaluated. Printed
paths, comments and assignments no longer count as source-line matches. JSON v1
adds stable check codes; text and JSON carry the same scope and next-step guidance.
All three static completion scripts include the new doctor targets.
Tool-version probes (including Neovim activation checks) now have a two-second post-spawn deadline and a 64 KiB combined stdout/stderr cap. Timeout, excessive output, nonzero exit or invalid output never establish a version from a partial prefix. Errors omit raw program output. Ghostty validation shares the same capture/cleanup implementation while retaining its five-second deadline. Only the probe's owned process group is terminated on timeout/overflow; spawn, filesystem IO, OS termination and deliberately detached descendants are outside the hard-time-bound guarantee. Minimum thresholds are unchanged.
Command lookup now requires a regular file and execute access for the current effective user/group, following ordinary symlinks. A non-executable same-name file, directory, FIFO or broken link no longer masks a later usable candidate. Existing PATH/fallback ordering and tool aliases are retained; Homebrew discovery uses the same check. Lookup does not execute candidates or inspect their contents. It is advisory, not an authorization boundary or a guarantee of executable format, interpreter availability or a later successful launch. If Neovim has no usable executable but a rejected/inaccessible candidate remains, activation reports an error and its explicit version diagnostic retains the candidate path, instead of suggesting that Neovim is simply missing.
Version parsing now requires a complete MAJOR.MINOR.PATCH on the first non-empty
output line, with an optional v prefix and valid prerelease/build metadata.
Known executable names (Neovim, Ghostty, Alacritty) must match that header. Invalid
headers are errors; later dependency/compiler versions cannot substitute for the
tool's version. Truncated numbers, extra components and invalid suffixes are not
repaired. Comparison follows SemVer precedence:
0.8.0-dev is below the Neovim 0.8.0 floor, while 0.8.1-dev is above it, and
build metadata does not affect the decision. This lower-bound check is not a
stable-release allowlist or a runtime compatibility guarantee for development builds.
Neovim availability checks use the executable found for the selected profile,
including home-local fallbacks outside PATH. Setup's activation stage probes it
once, then reuses the result for hook handling and its receipt. Missing or old
Neovim remains an ordinary skip; a failed version probe is an error, not an
instruction to reinstall. That failure prevents Neovim setup writes and makes
setup incomplete (earlier setup stages are not rolled back). Theme application
also reports the check failure instead of silently skipping it. Remembered manual
activation still bypasses the activation-stage probe entirely. Without an explicit
--check-version, doctor nvim remains file-only and does not launch a version probe.
Use slate doctor nvim --check-version [--json] to diagnose version problems without
rerunning setup. The optional version_probe JSON object reports supported,
unsupported, missing or failed, the selected executable and PATH/fallback
origin, the parsed version, minimum version and shared probe limits. The same
result appears in a version_probe check. Missing executables have a null binary;
failed checks have no accepted version. Non-UTF-8 binary display paths are marked
lossy. A failed check gives its reason, not an instruction to reinstall. Default
reports omit this object; schema version 1 and the existing file checks remain.
The flag is supported only for the explicit nvim target. It does not install
anything, write Slate configuration or change activation consent, and diagnostics
remain available while a Slate writer holds its lock. Explicit version checking
also works when automatic activation is disabled. It does launch the resolved
executable with --version: that executable is not sandboxed and may have its own
side effects. Only its parsed version/failure reason is shown, not stdout/stderr
bodies. A successful exit means the report was produced, not that every check
passed; inspect version_probe.status and the file-check statuses.
Alacritty application keeps the effective import list in place: a top-level
import takes precedence over general.import, matching the
upstream loader.
Slate appends missing managed entries without merging, replacing or migrating
the user's lists. New lists use general.import; inline tables and dotted keys
are supported. Font changes clear only the main file's font.normal.family
override when this operation supplies a managed font, retaining style, size and
other font faces. Already connected, unchanged main files are not rewritten.
User-level Alacritty TOML entry paths are checked in this order, following the documented Unix locations:
$XDG_CONFIG_HOME/alacritty/alacritty.toml$XDG_CONFIG_HOME/alacritty.toml$HOME/.config/alacritty/alacritty.toml$HOME/.alacritty.toml
Duplicate paths/directory aliases are collapsed. Setup reuses an existing entry
instead of creating a higher-priority shadow file; an obstructed preferred path
is surfaced, not silently bypassed. Detection, application, font reports and
doctor share this selection. System locations (including XDG_CONFIG_DIRS), YAML
and per-process --config overrides are not discovered or edited automatically;
check these manually before setup. With no user TOML candidate, setup retains its
default first-path creation behavior. ALACRITTY_SOCKET_PATH is not a config override.
Baselines, live-preview recovery and cleanup capture every user candidate, including missing files. Cleanup removes Slate imports from inactive candidates too, so later selection changes do not leave dangling hooks. An unsafe candidate can block capture/cleanup. A newly created higher-priority file during live preview is an external edit: Slate stops preview and preserves it. Setup's initial file creation is exclusive and never initializes through an already present dangling link; these checks are not a transaction against concurrent directory changes.
These Alacritty writers validate the selected import list and prepare the full
entry-file edit before publishing managed outputs. Entry files must be regular,
non-symlink UTF-8 TOML, with an 8 MiB input/output limit. Detected changes to the
entry's bytes, identity or ordinary permissions require retry. Publications are
still per-file, not an external-editor transaction; later I/O failures can leave
partial managed outputs. Font connection reports and doctor alacritty use the
same import precedence. These application diagnostics read bounded regular UTF-8 files,
report FIFOs/oversize input without opening them, and omit TOML source from errors.
OpenCode TUI theme edits preserve JSONC comments, whitespace and unrelated values,
including their numeric spelling. The adapter sets only the root theme to
"system" and adds $schema if absent. An already-system config is not rewritten
or backed up again by the adapter (the overall theme workflow may still capture a
restore point). Inputs must be regular, non-symlink UTF-8 files, at most 8 MiB;
malformed syntax, duplicate root keys or a non-string theme require manual repair.
Existing-file changes require backup and a source recheck; this is not a transaction
against external editors. SLATE_HOME isolation ignores host OPENCODE_TUI_CONFIG.
Cleanup uses the same parser and preserves even comment-only documents. It still
treats root theme: "system" as the integration setting, regardless of who set it;
inspect slate clean --dry-run first if you independently chose that theme.
slate doctor opencode [--json] reports Slate's selected entry, existing alternate
TUI candidates, unsafe files, invalid JSONC and system/custom/unset theme states.
It shares the adapter's path selection and edit-safety parser, omits configuration
values, and does not initialize settings, create backups or launch OpenCode. The
report is about that file, not the effective live theme: OpenCode also supports
project TUI configuration and custom paths.
The system setting uses the terminal's palette/default colors;
this doctor does not test terminal color support. Relative overrides are resolved
to an absolute path once per SlateEnv; relative_config reports that conversion
as information. Changes to cwd/process variables do not retarget the captured
environment. A new invocation still resolves relative paths from its own cwd;
use an absolute override for consistent selection across invocations. Directory
aliases are captured once in clean/import; final file links are not followed.
.. uses the real traversed directory, including directory links. An unresolvable
prefix or trailing / or /. is an explicit error, never a fallback to the default
file. Such errors remain visible as unresolved_config in doctor, do not break
unrelated diagnostics, and block relevant edits/checkpoints/clean before changing
configuration. File-only recovery uses its recorded targets even if the override changes.
Detection, application, doctor and recovery discovery share the injected override;
SLATE_HOME and SlateEnv::with_home ignore it. Integration doctors
(OpenCode, opacity, Kitty, Alacritty, Neovim, Zsh) retain target and checks in JSON and
add schema_version: 1, scope, and per-check path_is_lossy; OpenCode and opacity checks also
include stable code identifiers. A successfully printed report exits zero even
when checks contain warnings/errors; scripts should inspect checks[].status.
Text escapes terminal controls, lossy paths are labeled, and closed output pipes
are handled without panic. Diagnostics stay available during active writers or
pending preview recovery; they do not prove write permission or restore readiness.
slate doctor opacity [--json] compares the saved preset with Slate's four generated
Ghostty/Alacritty/Kitty opacity and blur files, using the same templates as the writers.
Missing or empty state is preset_unset, not an inferred default; unknown or non-UTF-8
state is preset_invalid. Recognized noncanonical spelling is reported separately.
Per-file codes distinguish output_matches, output_differs, output_missing,
output_unreadable and output_uncompared; use each check's path to identify the file.
Missing output is informational because an integration may be unused. A byte difference
(including comments or line endings) is not evidence of invalid syntax or a live fault.
It reads at most 4 KiB of state and 8 MiB per output, rejects final file links and
unsafe isolated-profile paths, and never prints file contents or starts a terminal.
Metadata checks also flag conflicting output aliases and blocked recovery-storage
layouts; a broken backup directory does not hide otherwise readable outputs.
No permissions probe, include-chain, user-override, compositor or live-appearance
validation is performed. Inspect manual edits first before using the suggested
slate config set opacity <preset> repair. The report is a set of observations,
not an atomic snapshot during concurrent edits, and follows the exit-zero contract above.
Before applying a shared code, try
slate import "slate://nord/jetbrains-mono/frosted/s,h" --dry-run.
The preview explains the request without reading your profile, discovering or
downloading fonts, or creating a lock. It works without HOME and during pending
recovery or another active writer. It is not a diff against your current settings
or a guarantee that application will succeed: font availability and write access
are checked only when applying. none in the theme/font/opacity positions means
keep the current value (null in JSON); the tool list replaces all three toggles,
so omitted tools are disabled and none disables all three. Actual imports still
apply in steps; a later failure does not automatically undo earlier changes.
Before changing settings or installing a requested font, actual import must save
a pre-import recovery point. It prints slate restore <id> --dry-run before
applying, and repeats the command on failure. Inspect that plan, then omit
--dry-run to restore the captured file bytes and ordinary Unix permissions;
files created by the import are removed when they were absent in the checkpoint.
These restores do not regenerate a theme over your original files. Coverage
includes saved settings, shared shell files and the selected adapters' config
outputs (including bat theme files), not installed fonts, external tool caches,
empty directories or running application state. Reload/restart tools as needed
after restoring. Recovery is not an automatic or concurrent-editor transaction.
Affected final symlinks, special files, unsafe isolated-profile paths, files over
8 MiB, or a total checkpoint over 64 MiB stop import before applying settings.
Recovery history is private under the profile's cache/backups directory; review
the plan before restoring, since later manual file edits would also be replaced.
export and screenshot sharing generate slate://v1/theme/font/opacity/tools.
The versioned font segment uses UTF-8 percent-encoding, preserving spaces, Unicode,
slashes, quotes and literal percent signs. Legacy four-part codes still work and
are not percent-decoded; recipients need a build supporting v1 to read new codes.
export --raw prints exactly one unstyled line; ordinary export suggests a safely
quoted preview command. Export reads only bounded regular settings files and
rejects invalid values, final symlinks and special files without printing their
contents. Unset theme/font/opacity is encoded as none; missing tool flags retain
Slate's defaults (Starship/highlighting on, Fastfetch startup off). Export does not
probe running tools or guarantee a consistent snapshot across concurrent writes.
slate share keeps existing images: it saves slate-share.png, then unused names
such as slate-share-2.png, without overwriting files, directories or links.
Capture and optional watermarking use separate private temporary files; cancellation
or an absent/invalid result never reports an image as saved. A failed watermark
warns and keeps the original capture. New exports are private (0600).
An existing XDG_PICTURES_DIR is used only when its resolved directory is inside
HOME; otherwise Slate tries an in-home Desktop, then HOME. Escaping aliases and
.. paths are ignored. Portal screenshot sources are copied, never deleted.
Image reads require regular, non-linked files up to 64 MiB and check the PNG
signature; this is not full image decoding. Optional watermark processing has a
10-second post-spawn deadline and 64 KiB combined stdout/stderr limit; interactive
capture itself has no such deadline. export --raw remains available without capture.
On Linux, Portal result subscription precedes the screenshot request so a fast response is not missed, including when an older portal returns a different handle. Only the request's returned path and original service owner are accepted. Service restart/disconnection ends waiting with an error; retry the command if needed. Connection/version/subscription setup has a shared 2-second budget; waiting for the screenshot interaction has no idle timeout. The connection remains open until the borrowed image has been copied. Details and fixture limits are in CONTRIBUTING.
Ordinary saved-setting reads also reject special files, broken links and invalid
UTF-8: tracking files are limited to 4 KiB, and config.toml / auto.toml to
256 KiB. A missing file still means unset/default; malformed TOML reports its path
and location without quoting saved contents. Invalid sound preferences keep
feedback silent. Flag changes preserve comments and other fields, and an auto-pair
update leaves the unspecified side intact. Valid links to regular dotfiles remain
readable, but Slate's atomic writers refuse final symlinks; export/import retain
their stricter link policy. Baseline/current/pre-restore snapshots have an 8 MiB
per-file and 64 MiB total bound, retain binary bytes and ordinary permission bits,
and publish only after all copies succeed. Reading a linked dotfile does not make
that link a permitted restore destination. These checks do not provide a transaction
against external editors or a timeout for an unresponsive filesystem.
list searches case-insensitively, ignoring common accents and word separators;
all search terms must match. Search and --appearance dark|light can be combined.
Search is for discovery only: applying a theme still requires its full ID or display
name. theme --list remains the unfiltered compatibility alias.
Unknown names return an error with up to three advisory IDs, including close spelling
matches; Slate never applies a suggestion automatically. Invalid names, incomplete
theme set forms, and combining a theme name with --auto are rejected before
profile initialization, lock creation or sound. This also applies to the set alias.
Valid selections still respect active writers and pending preview recovery. Error
messages remain on stderr under --quiet so scripts can diagnose a nonzero exit.
JSON schema version 1 includes the query, appearance filter, count, and themes
(ID, name, family, lowercase appearance, description, and optional auto-pair ID).
--json and --ids are mutually exclusive and never read saved settings. No matches
is a successful empty result (themes: [] / no ID lines); text explains how to broaden
the search. All catalog modes leave config/cache files untouched and stay available
during pending recovery. Piped output, NO_COLOR, and TERM=dumb contain no ANSI
escapes; exact palette swatches are shown only in truecolor terminals.
status does not initialize configuration, backup directories, or sound caches.
An unset or unknown theme is not presented as an applied default; invalid settings
are reported separately. Toolkit marks indicate availability, not integration health.
status --json reports saved settings, paths, warnings, and a content-free recovery
summary (clear, busy, active, pending, conflicted, or unreadable); busy
means another configuration operation holds the lock. JSON status does not probe
running tools. A successful status read can still contain warnings: inspect those
fields rather than treating exit code zero as a health check.
When a preview is unfinished, bare slate opens a recovery-first menu. Restoring
or keeping the current files requires explicit confirmation; Quit changes nothing.
A running preview cannot be recovered by this menu. Non-interactive invocations
only print recovery guidance. Direct theme, font, config, setup, import, restore,
and cleanup commands use the same writer lock: they fail before changing config
when another operation is running or a preview record remains. Status, doctor,
list, export, and restore preview/list commands remain available.
The preview-to-commit transition retains the lock without an unlock window, and nested calls within one operation reuse it. Locks coordinate Slate processes using the same cache root; use consistent HOME/XDG settings. They do not prevent manual edits or coordinate independently configured cache roots. Do not delete the lock file to bypass contention. Auto-theme keeps a pending appearance event through contention or preview recovery and retries when configuration writes are available again; disabling auto-theme cancels this pending work.
Auto-theme startup, status, stop, and Shell relaunch now use the same per-config
watcher identity. A private lifetime lock proves ownership; stop requests carry an
instance token, so stale records and unrelated same-named processes are not killed.
Profiles with different Slate config directories can share a cache without claiming
one another's watcher. Use the same cache root for a profile: independent cache roots
do not coordinate. Generated launchers pin HOME/XDG, ZDOTDIR, and NVIM_APPNAME from
setup; re-enable auto-theme after changing those bindings. Isolated SLATE_HOME
commands never start a desktop watcher.
The Rust watcher owns appearance events and theme writes. Its macOS helper only
emits events; GNOME uses an owned gsettings monitor, and portal sessions use D-Bus.
Pending appearance notifications are coalesced instead of accumulating a queue;
source failures remain visible. Native helpers accept only their backend's complete,
known records, with a 1 KiB per-record limit. Oversized records stop the source with
a content-free error; arbitrary helper stderr is not copied into the watcher log.
Idle native readers wait for output or cancellation without periodic polling.
Stopping the source cancels and joins its reader and terminates/reaps its owned
process group; it never signals a process discovered by name or an old PID record.
The managed Portal worker is also cancelled and joined on stop, including while
connecting or waiting without notifications. Connection and subscription setup
share a 2-second deadline (separate from initial backend discovery); readiness
means both signal subscriptions are installed, not merely that a thread started.
Normal idle watching has no such timeout. Portal owner loss/replacement, bus loss,
or malformed relevant signals stop the watcher with a content-free error; it does
not silently remain ready or reconnect. A later shell launch or re-enabling
auto-theme can start a fresh instance once the backend is available.
Private control files and logs live under $XDG_CACHE_HOME/slate/watchers/<profile-id>
(default ~/.cache/slate/watchers/...). Startup failures report that log path.
Existing lock/control files alone do not mean a watcher is running; do not delete
the runtime directory to bypass an active instance. Startup acknowledgement means
the Rust event loop is ready, not that a theme has already applied successfully.
Upgrade note: re-enable auto-theme to refresh the launcher and Shell hooks. Legacy watchers started by older binaries have no ownership record: they are not adopted or stopped by process-name matching. Stop any known old instance explicitly before using the new watcher; status reports only the new managed runtime.
slate doctor auto-theme [--json] compares the launcher/helper against this binary
and profile without executing them, reads the saved preference, and checks managed
lifetime-lock/control state. It distinguishes absent, starting, ready, stopping,
normally stopped, recorded failure, unconfirmed stale records, changing ownership,
and unreadable state. New instances save a private generation-matched exit receipt;
missing receipts do not prove a crash. Legacy artifacts do not prove legacy
processes are running. Refreshing a legacy/unrecognized launcher emits a warning
that untracked processes are not stopped.
The same report explains conditional dark/light choices using the runtime selection
policy and the same resolution JSON v1 fields as slate config pairing. A ready
watcher does not hide an unknown saved theme ID, invalid pairing document or unsafe
required current-theme file. Selection errors appear in issues even when automatic
switching is disabled; valid defaults, explicit cross-appearance overrides and catalog
self-pairs are not errors. Review slate config pairing before replacing or clearing
saved overrides. These separate file/runtime observations are not an atomic snapshot,
a desktop appearance probe or proof that either theme can be applied successfully.
The doctor reports issues, next steps, and the private log path, never log contents
or instance tokens. File reads are bounded and reject link/non-regular targets;
no desktop backend or process-list probe runs. Exit code zero means inspection
completed, not that the watcher is healthy: inspect issues, runtime.state,
installation, and resolution. Inspection does not acquire the configuration
writer and remains available while a write or pending preview recovery blocks edits.
Auto-theme diagnostic text escapes path control/directional characters. JSON retains
exact UTF-8 path strings; non-UTF-8 paths use a lossy display string with additive
path_is_lossy flags on launcher/helper and runtime.directory_is_lossy /
runtime.log_path_is_lossy flags. Unavailable runtime paths and their flags are null.
Lossy displays are not exact paths or proof that a watcher can use them. Both output
formats use the shared doctor writer: an early pipe-reader exit is normal (and does
not prove the reader received a complete report); other write errors still fail.
Restore previews compare the snapshot's files with their current contents and saved
Unix permissions without writing files. Blocked targets return a non-zero exit status.
Both text and JSON restore <id> --dry-run output tolerate an early pipe-reader exit,
but a blocked plan still fails even when the reader has closed; other output errors
also fail without a panic. Human previews escape names, paths and reasons using the
same display rules as history listing; JSON preserves its existing plan format and
exact recorded strings. This does not relax restore-record validation. Actual restore
still requires confirmation and stops before confirmation/file restoration if its
initial plan cannot be written, including a broken pipe (writer-lock metadata may
already exist). Read-only preview remains available during another writer or pending
preview recovery and never restores files or creates an undo point.
Ordinary explicit theme changes now save a pre-theme operation checkpoint before
writing the selected available adapters' files. It includes generated colors,
bat theme assets and shared Shell/theme-state outputs; picker commits also capture
the following opacity stage. Byte contents, ordinary permissions and prior absence
are saved, including newly created outputs. Unsafe/non-regular files and sources
over 8 MiB each or 64 MiB combined stop the change before theme writes. Unselected
tools and unrelated dotfiles are not included. Even a first theme change is not a
full pre-install baseline. Setup's broader baseline remains a separate record.
Quiet auto-follow and callers explicitly using an existing checkpoint/preview
journal do not create an additional pre-theme record.
Legacy-style named-theme restores still reapply the saved theme; the preview flags
this additional step, but does not list every regenerated theme file. Baseline,
pre-theme, pre-clean, pre-import, pre-opacity and undo checkpoints are file-only: they restore recorded
bytes, ordinary permissions and prior absence without regenerating a theme over
those files. This also applies when undoing an undo. Restore prints the new undo
checkpoint ID; list and preview JSON report may_regenerate_theme_files: false
for these file-only points. Finishing a file-only restore does not initialize an
otherwise absent Slate config directory.
An undo checkpoint covers the selected record's target files, not every possible effect of a later named-theme regeneration, external cache, font installation or running application. Check the plan before restoring, and reload tools as needed. In particular, bat's compiled cache is not restored or rebuilt automatically.
The confirmation prompt is bound to the in-memory file plan it displays. Before
restoring, Slate rechecks the parsed manifest, backup and current bytes, ordinary
permissions, file identities and resolved target paths. Changes detected while
you are deciding require a new preview and confirmation, without restoring files.
A second check runs after saving the undo checkpoint; if it detects a change,
restoration does not start and the error identifies the retained checkpoint.
Canceling discards the prepared plan. A separate --dry-run --json report is not
an authorization token: a later restore command prepares and confirms a fresh plan.
These checks do not lock out external editors after the final check, nor cover
the additional theme regeneration described above.
Recovery lists exclude invalid records from the selectable points, but report
their paths, reasons and inspection steps without deleting them. The picker also
shows these notes. Undo checkpoints are hidden by default with an explicit count;
--list --all includes them. --list --json (optionally with --all) emits schema
version 1, with points, issues, valid_count, hidden_undo_count and
ignored_entries. Each point includes its timestamp in Unix seconds, entry count,
tool names, baseline/undo flags and theme-regeneration behavior, never saved file
contents. Points sort newest-first, with ID ordering for equal timestamps.
The inventory checks record metadata, not current target bytes:
target_contents_checked is false. Exit zero means the scan completed, not that
every record is usable; inspect issues and preview an ID before restoring.
An inaccessible history directory fails with no partial JSON report. Names with
non-UTF-8 bytes have display-only lossy paths and no invented restore ID. Legacy
per-tool backup folders are ignored; timestamp-shaped folders missing a manifest
are reported as potentially incomplete/damaged, including captures still running.
Listing works during an active Slate writer or pending preview without reading
preferences, initializing sound or creating files. Like a directory listing, it
is not a consistent transaction across concurrent changes.
Directly previewing an invalid ID reports its error. A manifest must be a regular UTF-8 file of at most
1 MiB with no more than 512 entries, a matching directory ID and a valid UTC
timestamp. Linked point directories/manifests/backup files are rejected; backup
sources must be directly inside that point. Targets must be absolute file paths,
without parent traversal, duplicate directory-alias destinations or overlap with
recovery storage. Backup data and current-file comparisons each have an 8 MiB/file
and 64 MiB total bound. Unsafe current targets appear as blocked in a valid JSON
preview; invalid records fail before emitting a plan. Execution prepares all bytes
first and captures an undo checkpoint for that same target set. Ordinary directory
aliases and original-profile paths remain supported. These are structural checks,
not authentication of an edited backup or a transaction against external editors:
restore only trusted records after reviewing their target paths.
slate clean --dry-run [--json] projects file removals, rewrites, unchanged files,
and blocked targets using the same transformations as cleanup. It also lists
directories to remove, the preserved slate/user tier (not recursively scanned),
and planned watcher-stop / best-effort terminal-reload behavior. It never acquires
a writer lock, writes a backup/config/cache file, or controls a process, including
when another writer or an interrupted preview is present. File contents are never
printed; text paths escape terminal controls and lossy JSON paths are flagged.
JSON schema version 1 includes changes, directories_to_remove, summary,
scan_complete and issues. An unsafe/inaccessible tree produces an incomplete
report; known read/parse failures appear as blocked targets. Both return exit 1.
Best-effort parsers that retain malformed files report unchanged with a warning
instead. Exit zero is not permission to execute: snapshot_write_checked,
target_writes_checked and writer_and_recovery_checked are false. Backup and
target write permissions, active writers and pending recovery may still stop a
later clean. The report is an observation, not a replayable authorization or an
atomic snapshot of concurrent edits. Directory metadata and external process
state are outside file-restore coverage; installed tools are not uninstalled.
Preview source reads share snapshot limits (8 MiB/file, 64 MiB total). Target enumeration is capped at 512 file entries, with 64 nested levels / 4096 managed directories. These discovery limits also apply to execution; they do not impose a wall-clock timeout on an unresponsive filesystem. Explicit integration targets inside the preserved user tier are rejected.
Kitty cleanup matches complete literal include directives under Slate's managed
Kitty directory and Unix listen_on paths with the exact basename kitty-slate.
Neighboring path/key/socket names are preserved. Continuation lines are handled
as one directive, as described in Kitty's configuration reference.
Alacritty cleanup removes only owned import string tokens and their following
commas from root import or general.import (including dotted/inline forms).
All comments, retained values, quoting and line endings stay intact; empty arrays
and tables are retained. Files without matching imports are not rewritten.
Neither cleanup rule expands variables, resolves include-file aliases, follows
parent traversals, or evaluates generated/glob includes: inspect custom indirect
references manually. The same rules apply to the read-only clean preview.
Clean checks its target paths and saves a pre-clean snapshot before stopping the
watcher or removing files. This includes generated assets, all tmux config candidates,
both OpenCode TUI configs, and Neovim loaders/shims/state, but excludes the preserved
slate/user tier. Backup failure stops cleanup; later failures report the snapshot ID
instead of claiming success. Clean and restore under SLATE_HOME leave running
watchers alone; isolated or SSH cleanup does not reload graphical terminals.
Starship cleanup uses the same integration path as apply/backup, not STARSHIP_CONFIG.
Clean refuses symbolic-link targets/managed directories, isolated paths that escape
SLATE_HOME, and cache roots nested inside directories being removed. New snapshots
save file bytes and ordinary Unix permissions in private snapshot directories;
backup copies remain private even for executable originals. Valid older snapshots within the
read limits remain readable but
cannot recover permissions they never recorded. Empty directory metadata, ACLs,
extended attributes, and running application state are not restored. Cleanup is not
an atomic transaction against external editors: inspect slate restore <id> --dry-run
before restoring and use the same HOME/XDG profile throughout.
Managed marker blocks must have one ordered START/END pair on complete, matching
comment lines (#, Lua -- #, or Vim " #). Reversed, duplicate, incomplete,
inline or mixed-wrapper markers are rejected before editing that file, without
printing its contents. Inspect ambiguous markers manually; Slate does not guess
which user text to remove. Valid removal includes the full comment wrappers and
preserves surrounding bytes, including non-UTF-8 content and CRLF endings. Repeated
updates do not accumulate comment prefixes, and unchanged files are not rewritten.
A cleanup error may still follow earlier changes to other files; use its pre-clean
snapshot reference for recovery. Existing stray comments from older edits are not
automatically removed.
Manual theme application creates a file-only pre-theme checkpoint, including on
first application; this is not a full setup baseline. Setup stops if its pre-change
snapshot cannot be saved. If a pre-commit integration fails, Slate keeps the prior
global theme, shell environment and Neovim state and reports failure;
successful tool-file writes remain in place. Partial-apply
errors include a restore-preview command when a safety snapshot was created. The picker
also checks for partial failure before saving opacity or showing its success receipt.
If the picker's theme stage succeeds but saving opacity fails, Slate attempts
file-only recovery from that selection's pre-theme checkpoint while retaining
the write lock. It restores captured bytes, ordinary permissions and prior file
absence, including auto pairing, rather than regenerating a remembered theme or
opacity preset. No adapters, version probes or cache builders are rerun for recovery.
The command still fails and shows no selection-success receipt even when recovery
succeeds. Blocked, missing or partial recovery is reported with the original error
and available recovery IDs. A saved pre-recovery undo point may contain the partial
selection: preview it before using it. External caches and live windows are outside
file recovery; reload tools as needed. This is not a multi-file transaction.
The final shared-file phase is checked separately from tool adapters. Slate writes the shared shell configuration before recording the new current theme; if shell generation/publication or current-theme tracking fails, the apply report retains the failed stage, adapter results, and available recovery ID. The auto-theme pair and Neovim notification are not advanced on that failure. Earlier tool or shell files may already have changed: this is not an all-or-nothing transaction. Fix the reported path/settings before retrying, or inspect the saved restore point first. Quiet mode still returns a nonzero exit status and the recovery hint. A restore whose subsequent theme reapplication fails also reports failure and its undo ID.
Selected, available Neovim notifications run once after shared Shell files and
the current-theme record are saved. A pre-commit failure reports ThemeNotCommitted
instead of claiming Neovim was applied or missing. Neovim-only theme changes still
work. If its notification write fails after commit, the operation reports a failed
Neovim result and explicitly says the new theme was already saved; it does not roll
back the theme/auto pair or silently retry the notification. Inspect the checkpoint
before restoring, or fix the write issue and retry. When Neovim is not a ready
selected adapter, the existing shared editor-state hook remains best-effort and
its failures are retained as warnings. Direct adapter/registry calls remain
immediate APIs without a global commit. A state-file publication is not proof of
editor receipt/rendering, and does not guarantee exactly one filesystem event.
Automatic theme application (theme --auto and the background watcher's apply callback) consumes
the resolved pair without detecting appearance again or rewriting auto.toml.
Fallbacks do not create a pairing file. Legacy restore reapplication also preserves
the restored pair. Manual selections still remember the slot for the detected
system appearance when auto-theme is enabled. If that preference update fails,
Slate warns that the theme was already saved and the next automatic switch may
use the previous pairing; it does not roll back or suppress editor notification.
Retained pairing, editor and reload warnings go to stderr even in quiet mode and
picker commits, and in the background watcher's log. These warnings alone do not make an otherwise successful apply
exit nonzero; required writes and selected adapter failures still do.
Short desktop-appearance commands (defaults / GNOME gsettings get) have a
2-second post-spawn deadline and an 8 KiB combined stdout/stderr cap. Slate accepts
known values, not a substring such as “dark” inside an error; macOS's recognized
missing AppleInterfaceStyle diagnostic still means Light. Failed, invalid or
timed-out queries stop theme --auto before theme files are written. A manual
selection already saved before its pairing query fails stays saved, with a warning.
Linux Portal Settings version/read queries have a separate 2-second asynchronous
deadline covering connection, proxy creation and reply. A successful Portal answer
is used directly; an available GNOME fallback can add its own query deadline.
For older Portal implementations, a ReadOne unknown-method reply triggers one
legacy Read request within the same deadline, decoding its extra variant layer.
Access errors, timeouts and malformed replies do not trigger legacy retries. Query
errors identify the stage and a safe error category without exposing response data.
Settings and Screenshot Portal version detection uses the protocol's lowercase
version property, including older backends. Screenshot version probes also use
a 2-second asynchronous query budget.
Missing session buses/services or unsupported settings still allow the headless
Light fallback, but access/protocol failures and timeouts are not silently Light.
These are query limits, not a whole-command or OS/filesystem timing guarantee.
Long-running appearance monitors have no idle timeout (Portal subscription setup
is bounded separately); interactive screenshot requests are not given this short
timeout. Native output and appearance/screenshot D-Bus response details are omitted from errors.
When file contents need to change, slate config set opacity <preset> first saves
a pre-opacity checkpoint for the four generated Ghostty/Alacritty/Kitty
opacity/blur files and current-opacity.
It prints a restore-preview command, including in quiet mode. The saved preset is
updated only after all four outputs succeed; a later failure reports its stage and
available recovery point without claiming earlier writes were rolled back. Final
links, unsafe targets, escaping isolated paths, overlapping backup storage and
conflicting terminal-output aliases are rejected. Ordinary in-profile directory
aliases remain supported; these checks do not lock out external editors.
This checkpoint restores file bytes, permissions and prior absence, not live window
state or unrelated settings. Imports reuse their wider checkpoint, theme/picker
snapshots also cover these generated files, and previews keep using their journal
without creating opacity checkpoints. Reload is best-effort and only attempted
when the captured session permits local terminal control, never for SSH/isolation.
Repeated opacity settings compare all generated file bytes and the saved preset, not just the preset name. Identical files keep their identity, modification time and permissions; an entirely unchanged standalone apply creates no new checkpoint. Missing or altered files are repaired individually. Reads remain bounded (8 MiB per managed output, 4 KiB for the preset), and unsafe/unreadable sources fail rather than being treated as no-ops. Files are rechecked for content, identity, permissions and directory redirection before their write/skip decision. These are per-file checks, not a multi-file transaction; later failures can leave earlier writes. The same templates and skip rules apply inside terminal adapters. Requested local reloads still run on a disk no-op: the saved files do not prove the live window state.
How it works
Slate composes managed config files alongside your existing setup rather than replacing your dotfiles.
~/.config/slate/config.toml # preferences (theme, font, toggles)
~/.config/slate/auto.toml # dark/light theme pairing
~/.config/slate/managed/<tool>/* # generated assets slate owns
~/.config/<tool>/... # your configuration, plus integration settings
For Ghostty: config-file = .... For Kitty/Alacritty: managed include/import entries. For zsh/bash/fish: a removable marker block in the shell rc. For Neovim: a pcall(require, 'slate') marker block in init.lua (init.vim works too) that falls back silently if slate is uninstalled. Slate-owned files stay slate-owned; yours stay yours.
slate completions <bash|zsh|fish> prints a script; it does not install files or edit
your shell startup configuration. Save it once and load it with your shell:
| Shell | Save the generated output as | Activation |
|---|---|---|
| Zsh | _slate in a directory on $fpath |
Add that directory before your existing compinit call. |
| Bash | A file such as slate.bash |
Source it from your interactive Bash startup file. |
| Fish | slate.fish in your Fish config's completions directory |
Fish loads it automatically. |
For example, in Zsh:
mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}/zsh/site-functions"
slate completions zsh > "${XDG_DATA_HOME:-$HOME/.local/share}/zsh/site-functions/_slate"Put fpath=("${XDG_DATA_HOME:-$HOME/.local/share}/zsh/site-functions" $fpath) before
the existing compinit/framework initialization in ${ZDOTDIR:-$HOME}/.zshrc, then
open a new shell. If you do not already initialize completion, use
autoload -Uz compinit followed by compinit there; do not duplicate a framework's
initialization. These steps are optional and must be performed explicitly.
Candidates include public commands/options, canonical theme IDs and appearance values. Scripts do not launch Slate on startup or Tab, and do not enumerate live fonts or restore points. Generation does not read settings or need HOME, and works while configuration is locked or recovery is pending. Regenerate the saved script after upgrading Slate; avoid putting the generator itself in a startup file.
20 variants across 9 families: Catppuccin · Solarized · Tokyo Night · Rosé Pine · Kanagawa · Everforest · Dracula · Nord · Gruvbox.
All 20 variants — palette gallery
Regenerate with scripts/render-theme-gallery.sh; future drift is caught by tests/docs_invariants.rs. Swatch order, left to right: background · foreground · brand accent · red.
| Family | Variant | ID | Appearance | Palette |
|---|---|---|---|---|
| Catppuccin | Catppuccin Frappé | catppuccin-frappe |
Dark | |
| Catppuccin | Catppuccin Latte | catppuccin-latte |
Light | |
| Catppuccin | Catppuccin Macchiato | catppuccin-macchiato |
Dark | |
| Catppuccin | Catppuccin Mocha | catppuccin-mocha |
Dark | |
| Solarized | Solarized Dark | solarized-dark |
Dark | |
| Solarized | Solarized Light | solarized-light |
Light | |
| Tokyo Night | Tokyo Night Dark | tokyo-night-dark |
Dark | |
| Tokyo Night | Tokyo Night Light | tokyo-night-light |
Light | |
| Rosé Pine | Rose Pine Dawn | rose-pine-dawn |
Light | |
| Rosé Pine | Rose Pine Main | rose-pine-main |
Dark | |
| Rosé Pine | Rose Pine Moon | rose-pine-moon |
Dark | |
| Kanagawa | Kanagawa Dragon | kanagawa-dragon |
Dark | |
| Kanagawa | Kanagawa Lotus | kanagawa-lotus |
Light | |
| Kanagawa | Kanagawa Wave | kanagawa-wave |
Dark | |
| Everforest | Everforest Dark | everforest-dark |
Dark | |
| Everforest | Everforest Light | everforest-light |
Light | |
| Dracula | Dracula | dracula |
Dark | |
| Nord | Nord | nord |
Dark | |
| Gruvbox | Gruvbox Dark | gruvbox-dark |
Dark | |
| Gruvbox | Gruvbox Light | gruvbox-light |
Light |
Built with AI assistance, with every change reviewed and tested by a human before it lands.
MIT.
Built on top of great work from others: Ghostty · Kitty · Alacritty · Neovim · Starship · bat · delta · eza · lazygit · fastfetch · tmux · zsh-syntax-highlighting · Nerd Fonts.


