What config is — and is not — for
A workspace is a directory. How agents behave inside it belongs to the tree itself
(CLAUDE.md / AGENTS.md, read natively by the provider CLIs),
and what groups directories into one workspace belongs to resolvers. The config file
therefore holds only user preferences about Stormlight's own behavior: which
provider and permission mode to reach for, how the UI looks, where the logs go. It never
defines workspace semantics and never injects agent context.
Principles
- Zero-config. The file is optional; every setting has the default
the binary ships with.
brew install→stormlightmust keep working with no file present. - One precedence rule everywhere:
flags > environment > config file > built-in defaults. - Config never breaks determinism. Config keys select among behaviors Stormlight owns (which mode, which provider, which binary) — they do not open the door to arbitrary per-machine drift in how managed agents run.
- Config cannot silently escalate permissions. Nothing a repository checkout contains may raise an agent's permission mode (see "Project-local config" below).
File location
~/.config/stormlight/config.toml, honoring
$XDG_CONFIG_HOME. Everything Stormlight owns lives under one directory each
for config and state — dev-tool convention (git, gh, nvim do the same on macOS), and one
directory to document:
~/.config/stormlight/
config.toml # user configuration
resolvers/ # executable workspace resolvers
~/.local/state/stormlight/
workspaces.json # workspace catalog
sessions.jsonl # session history log
Format
TOML, parsed with pelletier/go-toml/v2. Rationale: comments (a config
file that can't explain itself is a bug), obvious sectioning, no significant whitespace,
the standard choice in the Go/Homebrew tooling ecosystem. JSON has no comments; YAML has
too many footguns for a file this small.
Schema
# ~/.config/stormlight/config.toml — all keys optional.
[defaults]
provider = "claude" # codex | claude | shell
mode = "edits" # ask | edits | auto
[ui]
rows = "compact" # compact | expanded
[log]
level = "info" # debug | info | warn | error
# file = "…" # overrides the default log location
[tools]
# yazi = "/opt/homebrew/bin/yazi" # override PATH lookup
# nvim = "/opt/homebrew/bin/nvim"
# Per-workspace overrides, keyed by workspace root.
[workspaces."/Volumes/repos/stormlight"]
mode = "auto"
provider = "claude"
# Provider adapter tweaks. extra_args append after Stormlight's own
# flags, so they can refine but not remove lifecycle hooks.
[providers.codex]
# binary = "codex"
# extra_args = ["--model", "o4"]
# User-defined providers. args is an exec-style array with a {task}
# placeholder — never a shell string.
[providers.aider]
# label = "Aider"
# binary = "aider"
# args = ["--message", "{task}"]
# [providers.aider.mode_args]
# auto = ["--yes-always"]
Custom providers appear in the New Agent picker and dispatch like any other.
Lifecycle integration is the public contract: managed processes receive
STORMLIGHT_ID and can report state with stormlight event from
any hook mechanism the CLI provides. Stormlight never reimplements an agent.
Wiring
internal/config:config.Load()→Configstruct with the defaults filled in, called once before command construction.- Cobra flag defaults are initialized from the loaded config, so the precedence rule falls out of the existing machinery instead of being reimplemented per flag.
- Validation errors name the key and file
(
config.toml: defaults.mode: invalid permission mode "always"), and a broken config falls back to defaults with a visible warning rather than refusing to start — the dashboard is also how you'd notice the problem. stormlight configprints the effective merged config and the file path;stormlight config initwrites a fully commented template.
Project-local config (deliberately deferred)
A .stormlight.toml in a repo root ("this project defaults to codex") is
attractive for teams but dangerous: a cloned repository must never be able to grant
itself mode = "auto". If it is added later, it needs direnv-style trust
("stormlight noticed project config, run stormlight trust to apply") and a
hard rule that permission mode can only be lowered by untrusted project files.
For now, overrides live in the user's own config file, keyed by path.