Push, not poll
macOS notifications, the XDG portal, and a Windows registry watch react when the OS changes.
Light / dark, resolved
A local daemon resolves one effective theme from your OS, an offline sun schedule, or a manual override—then broadcasts it to your terminal, tray, and Neovim.
$ theme-agent status
mode auto
effective dark (source: sun)
system unavailable
next 07:42 sunrise → light
$ theme-agent subscribe --json
{"event":"theme_changed","effective":"light","previous":"dark","source":"system","mode":"auto","timestamp":"2026-06-15T22:08:29Z"}
macOS notifications, the XDG portal, and a Windows registry watch react when the OS changes.
NOAA equations, DST-aware local time, and explicit support for polar day and polar night.
CLI, tray, and Neovim stay in sync over per-user, local NDJSON IPC.
launchd, systemd --user, or a logon task. It starts at login and never needs admin.
A fixed priority turns every input into light or dark: manual > system > sun > fallback.
light
Always resolves to light as a manual override.
theme-agent set lightdark
Always resolves to dark as a manual override.
theme-agent set darksystem
Uses the current OS preference, then the configured fallback.
theme-agent set systemsun
Uses the offline sun calculation, then fallback. A location is required.
theme-agent set sunauto
Tries the OS preference, then offline sun calculation, then fallback.
theme-agent set autoSun mode · offline
The daemon uses hand-rolled NOAA solar equations. jiff keeps the calculation timezone- and DST-correct at any longitude.
Polar day and polar night are first-class states. Coordinates come only from your config; no network or geolocation lookup is involved.
[location]
latitude = 51.5072
longitude = -0.1276
[sun]
elevation = -6 # civil twilightThe daemon owns the answer. Every surface subscribes to the same local state.
Editor
require("theme_agent").setup({
-- colorschemes = { light = "dawnfox", dark = "nightfox" },
})Desktop
theme-agent
Hooks
[hooks]
on_start = ["notify-send", "Theme", "{theme}"]argv only — no shell, ever
IPC
{"event":"daemon_ready","effective":"dark","mode":"auto"}
{"event":"theme_changed","effective":"light","previous":"dark","source":"system","mode":"auto","timestamp":"2026-06-15T22:08:29Z"}
{"event":"config_changed","timestamp":"2026-06-15T22:08:30Z"}Socket 0600. Per-user only. No TCP.
Install
$ rustup update stable
$ cargo +stable install --locked --git https://github.com/Ajaymamtora/theme-agent theme-agentRegister the service
$ theme-agent service installHeadless
$ cargo +stable install --locked --no-default-features --git https://github.com/Ajaymamtora/theme-agent theme-agentFirst run
$ theme-agent status
$ theme-agent set sun
$ theme-agent subscribe