modisa.Documentation
Configuration

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

sh
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

toml
[keys]
zoom = "f"                 # <action> = a key after the prefix
split-right = ["v", "|"]   # or several
help = ""                  # or none

An 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.

sh
modisa config reset-keys   # [keys] and [plugin_keys] out, prefix "C-b"; the old file in config.toml.bak

Every setting

toml
# 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"

Commands, keys, agents, settings…