DOCUMENTATION / Reference
Configuration
config.toml, with every setting and its default.
Where it lives
~/.config/modisa/config.toml. The settings page edits it for you; modisa config edit opens it in your editor. Changes apply live; a change that doesn't parse is left out, with a toast saying where.
Checking it
modisa config check # file:line: error|warning: setting: what's wrong
modisa config check --json # {file, exists, ok, problems: [{level, key, message, line, column}]}It needs no session. A file that doesn't parse, or a value modisa can't use (an unknown theme, a sidebar width outside 20–48, a notify kind or sound that doesn't exist, a key that can't be bound), is an error and exits 1. A setting modisa doesn't know is a warning, since it's ignored, and exits 0.
Keys
[keys]
zoom = "f" # <action> = a key after the prefix
split-right = ["v", "|"] # or several
help = "" # or noneAn action set in [keys] has only the keys given there, taking each from whatever had it; the rest keep modisa's. The keyboard guide (Ctrl+B ?) shows every binding with its action's name. A key is one character (H is shift+h) or left, right, up, down, home, end, pageup, pagedown, f1–f12. x (close pane), d (detach) and Escape are how you get out of anything a plugin opens, so they can't be given to anything else. A key [keys] frees can go to a plugin; one it takes can't.
modisa config reset-keys # [keys] and [plugin_keys] out, prefix "C-b"; the old file in config.toml.bakEvery setting
# modisa config — changes apply live (Ctrl+B s opens the settings page)
prefix = "C-b" # C-<key>
theme = "ion" # ion, tokyonight, catppuccin-mocha, gruvbox, nord, dracula, bearded-* (see settings)
[sidebar]
visible = true
width = 26 # 20 to 48 columns, at most a third of the terminal; dragging its edge sets it
agents = "" # a plugin whose sidebar section takes the AGENTS list's place ("radar"); "" keeps modisa's
logos = "auto" # agents' logos where the terminal can show them (modisa logos); "on", or "off" for plain marks
graph = false # true draws the AGENTS list as a git graph of its tabs; the dots stay either way
position = "left" # left or right
sections = ["agents", "plugins", "commands"] # their order; "plugin:<name>" for one plugin's; leave one out to hide it
sort = "attention" # attention (who needs you first), name, created
show = "space" # space (this space's agents), tab, all
# row = ["{icon} {name}", " {agent} · {state}"] # agents as a flat list of these formats (see CUSTOMIZE.md)
[status] # the bottom row, besides its buttons
agents = true # how many agents are working and need you
panes = true # how many panes this tab has
theme = false # the theme's name (click it to change theme)
# left = "#[bold $accent] modisa #[] {space} {?blocked|#[$blocked]{blocked} need you|}" # the row as your formats
# right = "{git.branch} #[$dim]{clock:%H:%M}"
[tabs]
position = "top" # top, bottom or hidden
# format = "{tab.index}:{tab}"
[window]
# title = "{space} · {name}" # the terminal's title; "" leaves it alone
[git] # the active space's repository, on the right of the status row
status = true # its branch (green when clean and in step with its upstream)
repo = true # the repository's name before it
counts = true # ↑ commits to push, ↓ commits to pull
changes = true # ● files changed
[panes]
border = "single" # single, rounded, double, heavy, light_double_dashed … quadrant_outside, full, none
dim_unfocused = 0.0 # 0 to 0.8: the other panes' text mixed toward the background
# title = "{name} {?agent|{icon} {agent} {state}|}"
title_position = "top_left" # top_left, top_center, top_right, bottom_left, bottom_center, bottom_right
[mouse] # clicks, drags, the wheel and right-click always work (Shift-drag selects text natively)
hover = true # the pointer resting on a row of a menu, picker or the settings page selects it
[notify] # toast, system, sound, bell — when an agent you're not looking at…
blocked = ["toast", "system", "sound"] # …needs you
done = ["toast"] # …finished
working = [] # …started working
unread = false # a tab keeps a • after an agent in it needed you or finished, until you look at it
click = "none" # "focus": clicking a system notification focuses the pane (needs terminal-notifier on macOS)
# [notify.codex] # one agent's own: blocked, done, working over the above
# done = []
[sound] # the sound each event plays (cuelume): chime, sparkle, droplet, bloom,
volume = 0.7 # whisper, tick, press, release, toggle, success, error, page, loading,
blocked = "chime" # ready, pulse, scan, arrival
done = "success"
working = "loading"
pack = "" # a directory of your own sounds named after the events: blocked.wav, done.mp3, …
# [sound.claude-code] # one agent's own: a sound's name or a file, per event
# done = "~/sounds/ding.wav"
[indicators]
style = "symbols" # symbols ! ◆ ✓ ○ · dots ● ● ● ○ · letters B W D I
tab = true # badge on tabs with an agent that needs you
pane = true # in pane border titles
sidebar = true
[pane_labels]
agent = true # agent and state in the pane's border title
[update]
check = true # tell me when a new modisa is out (modisa update installs it)
channel = "stable" # stable, or staging for prerelease builds
[messaging]
max_hops = 10 # stop reply chains after this many hops
per_minute = 5 # per sender→recipient pair
[permissions] # allow, ask, deny — for agents acting on panes they didn't create
keys_foreign = "ask"
close_foreign = "ask"
run_foreign = "ask"
# [agents.claude-code]
# launch = "claude --model opus"
# Programs started with the session server, with $MODISA_SOCKET set. See examples/plugins.
# [[plugin]]
# run = "my-plugin --socket $MODISA_SOCKET"
# A plugin's keys (after the prefix) are the ones its plugin.json asks for unless you change them here:
# "<plugin>.<action or pane>" = "K", or "" to turn one off.
# [plugin_keys]
# "attention-log.log" = "A"
# Plugins add to modisa's status row, tabs, pane borders, sidebar and menus. Where one asks to draw instead of modisa,
# say who may: a plugin's name, or "builtin" for modisa's own. With no entry, the first plugin (by name) that asks does;
# modisa plugin list shows who holds what. [sidebar] agents is "sidebar.agents". The slots: examples/plugins/CHROME.md.
# [slots]
# "agent.row" = "radar"
# "pane.title" = "builtin"
# modisa's own keys (after the prefix): "<action>" = "K", or a list of keys, or "" for none. An action set here
# loses its default keys; the keyboard guide (prefix ?) names every action. x (close pane), d (detach) and escape
# can't be given away. modisa config check finds mistakes; modisa config reset-keys puts the defaults back.
# [keys]
# zoom = "f"
# split-right = ["v", "|"]
# A name for a list of actions, run in order; bind it like any action ([keys], [root_keys], a mode).
# [actions]
# "dev-layout" = ["split-right", "focus-left", "zoom"]
# Keys that work without the prefix (they never reach the panes, so each needs C- or M-, or is an f-key).
# [root_keys]
# focus-left = "M-h"
# focus-right = "M-l"
# palette = "M-return"
# A key mode: `enter` after the prefix, then its keys work alone until escape (sticky = false: one key, then back).
# [modes.resize]
# enter = "r"
# keys = { resize-left = "h", resize-right = "l", resize-down = "j", resize-up = "k" }
# Your own commands, in the palette and on keys. `in`: split, split-down, tab, zoomed or background. {cwd}, {pane},
# {name}, {space}, {tab}, {session} and each prompt's name are filled in (prompt answers shell-quoted).
# [[command]]
# name = "Run tests"
# key = "T"
# run = "bun test {filter}"
# prompts = [{ name = "filter", title = "Test filter" }]
# Commands run on what happens, with the event as JSON on stdin; {field} fills in one of its fields. Those on
# message.send, agent.spawn, pane.keys, pane.run or notify run first and may stop it or change it (see CUSTOMIZE.md).
# [[hook]]
# on = "agent.state"
# when = "to == 'blocked'"
# run = "say {name} needs you"
# How --remote starts modisa on the far side of ssh. Set an absolute path when it isn't on the
# PATH of a non-interactive ssh shell (~/.local/bin often isn't).
# remote_command = "modisa"