Customization¶
Config files¶
smelt loads Lua from a fixed sequence of files. Each one is optional; if it doesn't exist, smelt moves on.
| Order | File | What it's for |
|---|---|---|
| 1 | ~/.config/smelt/early.lua |
Runs before argv is parsed. Restricted API — only smelt.cli.register_flag and smelt.builtins.disable. See Early-phase config. |
| 2 | .smelt/early.lua |
Project-scoped early phase. Same restrictions; requires trust. |
| 3 | ~/.config/smelt/init.lua |
Your main config — providers, settings, permissions, MCP, keymaps, commands, custom tools. |
| 4 | ~/.config/smelt/plugins/*.lua |
Loaded after init.lua. One file per plugin. |
| 5 | .smelt/init.lua |
Project-local override. Requires trust. |
| 6 | .smelt/plugins/*.lua |
Project-local plugins. Requires trust. |
~/.config/smelt honors $XDG_CONFIG_HOME. Override the init.lua path with
--config <path>. If no config exists on first launch, the setup wizard
creates one for you.
Project-local files (.smelt/*) are gated by the trust prompt — accept the
directory the first time you open it. Use them for repo-specific keymaps,
slash commands, permission rules, or MCP servers without polluting your
global config.
The Getting Started guide covers basic provider setup.
See the Configuration Reference for every
provider/setting field, and the Plugin Authoring guide for
writing larger extensions against the smelt Lua API.
Runtime Settings¶
Toggle settings at runtime with /settings, set defaults in init.lua by
assigning to smelt.settings, or override from the CLI with --set key=value:
See the Configuration Reference for every key and default.
Per-plugin Model Preferences¶
Bundled plugins (title generation, compaction, prediction, /btw,
web_fetch) read their preferred model from
smelt.model.preferred("<name>") and fall back to the primary when unset:
smelt.model.preferred("title", "openai/gpt-5-mini")
smelt.model.preferred("compact", "anthropic/claude-haiku-4-5")
The model must be registered under a provider. Custom plugins can pick any name they like to expose the same override pattern to users.
Themes¶
Built-in accent presets:
ember·coral·rose·gold·ice·sky·blue·lavender·lilac·mint·sage·silver
Change at runtime with /theme, or accept a raw ANSI value (0–255). The task
slug color is separate — change it per-session with /color.
Custom Colorschemes¶
smelt.theme.use("name") loads
runtime/lua/smelt/colorschemes/<name>.lua (or your own file on the Lua
package.path) and applies it. A colorscheme returns a ThemeSpec
table: a flat map keyed by highlight-group name. Each value is either a
StyleDecl table ({ fg = ..., bg = ..., bold = true }) or a string
referencing another group in the same spec.
-- ~/.config/smelt/lua/smelt/colorschemes/mytheme.lua
return {
SmeltAccent = { fg = { ansi = 208 } }, -- ember
SmeltMuted = { fg = { ansi = 244 } },
SmeltUserBg = { bg = { dark = { ansi = 236 }, -- light/dark branches
light = { ansi = 254 } } },
Comment = "SmeltMuted", -- alias another group
}
Color values support { ansi = N } (256-color slot), { rgb = { R, G, B } }
(sRGB triple), or a { dark = ..., light = ... } pair that resolves
against the detected terminal background.
Theme APIs touch live TUI state, so they can't run at the top level of
init.lua (the TUI isn't up yet). Defer the call until the session is
ready:
smelt.theme.set(group, style) tweaks a single group on top of the
active scheme (same StyleDecl shape as a value in the map).
smelt.theme.snapshot() dumps every group's resolved style.
smelt.theme.is_light() reports the detected background.
Keymaps¶
Bind chords with smelt.keymap.set(mode, chord, handler). Modes are
"n"|"i"|"v"|"" (or the long forms normal/insert/visual); "" binds in
every mode.
Built-in chords are listed in the Keybindings Reference.
Custom Commands¶
Markdown commands¶
Drop a .md file in ~/.config/smelt/commands/ and it becomes a slash
command. For example, ~/.config/smelt/commands/commit.md:
---
description: commit staged changes
model: openai/gpt-4o
temperature: 0.2
reasoning_effort: low
bash:
allow: ["git *"]
---
Create a conventional commit for the staged changes.
Staged diff:
!`git diff --cached`
Recent commits for style reference:
!`git log --oneline -5`
Type /commit and the agent receives the evaluated prompt with shell outputs
inlined. Arguments are appended: /commit fix typos.
See Custom Commands for all frontmatter fields and template syntax.
Lua commands¶
Register from init.lua with smelt.cmd.register:
smelt.cmd.register("hello", function(arg)
smelt.notify("hello, " .. (arg or "world") .. "!")
end, { desc = "say hi" })
Reacting to events¶
Subscribe to engine and UI events with smelt.cell(name):subscribe(handler):
smelt.cell("turn_end"):subscribe(function(data)
if not data.cancelled then smelt.notify("done") end
end)
Events include turn_start, turn_end, mode_change, model_change,
tool_start, tool_end, session_start, input_submit, shutdown, and
more. See the Lua API reference for the full list.
Statusline¶
Append your own segments alongside the built-in slug, mode, model, cost, and position spans:
smelt.statusline.register("clock", function()
return { text = os.date("%H:%M"), fg = 245, priority = 2 }
end, { align = "right" })
Sources render left-to-right in registration order; { align = "right" }
sends a source's segments to the right strip by default.
Skills¶
Skills are on-demand knowledge packs the agent can load during a conversation.
Place a SKILL.md file in ~/.config/smelt/skills/<name>/ (global) or
.smelt/skills/<name>/ (project-local). See the
Configuration Reference for the full
format.
External Tools (MCP)¶
Connect external tool servers via the
Model Context Protocol. Servers run as child
processes and their tools become available to the agent. Register them in
init.lua with smelt.mcp.register — see the
Configuration Reference
for setup.
Inspect connected servers at runtime with smelt.mcp.list(),
smelt.mcp.tools(server?), and smelt.mcp.status(name). Useful for
statusline indicators and conditional keymaps.
Provider Middleware¶
Hook into the provider request/response cycle to log, redact, or rewrite payloads:
smelt.provider.middleware({
on_request = function(messages)
-- inspect or return a replacement messages array
end,
on_response = function(message)
-- inspect or return a replacement assistant message
end,
})
Hooks fire in registration order; each hook sees the previous one's
replacement. To observe streaming tokens without mutating mid-stream, use
smelt.cell("stream_delta"):subscribe(...). See the
smelt.provider reference for details.
Early-phase config¶
early.lua runs before the binary parses argv, so it's the only place where
you can declare new CLI flags or opt out of bundled modules. The rest of
init.lua runs as normal afterwards.
-- ~/.config/smelt/early.lua
smelt.cli.register_flag({ name = "experimental", kind = "boolean" })
smelt.builtins.disable("tools.web_fetch")
Only smelt.cli and smelt.builtins are available here — calling anything
else raises. See smelt.cli and
smelt.builtins for the full surface.
Custom Instructions (AGENTS.md)¶
Place an AGENTS.md file in your project root (or ~/.config/smelt/AGENTS.md
for global instructions). Its contents are automatically appended to the
system prompt for every conversation in that directory.
Use it for project conventions, coding standards, or any persistent context
the agent should know. Disable with --no-system-prompt.