Configuration
Lua, and a program rather than a data file: it may probe the machine, loop and branch. One entry point for what you name, and three directories for what you install.
The entry point
-- ~/.config/magi/init.lua — what you name explicitly. -- What you *install* is discovered: see writing a tool. magi.load("tools.lua") magi.model = "openrouter/anthropic/claude-sonnet-4.6" magi.confine = true -- refuse paths outside the session root magi.allow = { … } -- answered in advance, not under pressure
What it takes
| setting | does |
|---|---|
magi.model | which model, as magi models prints it |
magi.memory · magi.tools · magi.melchior | as a string, which program fills that role — see swapping a program |
magi.confine | whether read, write and edit refuse paths outside the session's directory |
magi.allow | permissions granted in advance, so they are not asked about |
magi.trusted | directories whose .magi.lua is as good as this file |
magi.env | environment every process magi starts is given, on top of what it inherits |
magi.project | what this session is called, in the name other sessions see |
magi.agent_talk | how far one session may reach another |
magi.ui | every colour, glyph and measurement the screen draws with |
magi.casper · magi.melchior · magi.balthasar | as a table, settings handed to that sibling, in its vocabulary — one place to edit rather than two. For the tools role the table is named after whichever program fills it. |
A project's own file
A .magi.lua arrives with a checkout. Cloning a repository and running magi in
it must not be enough to add a tool — a tool names a command to run — or to widen what runs
without asking.
| a project file may | and may not |
|---|---|
| choose a model the machine already offers | declare a tool |
| set ordinary settings | touch confine, allow or trusted — fatal, not a warning |
| load its own files | name its own directory as trusted |
| tune a sibling through its settings table | name the program that fills a role — fatal, since it would run with the session’s authority |
The one way past it is from your own configuration: naming a directory in
magi.trusted makes its project file as good as yours. A decision made once, in
advance, in the file only you can edit. Without a way to say yes, the rule would be worked
around instead of used.
Configuring a sibling through magi
magi asks each sibling what it takes, hands over what your config named, and reports anything the far side would not accept — a coordinator and a sibling disagreeing about what a name means is worth a line on stderr.
magi.balthasar = { promote_floor = 0.6 }
magi.melchior = { max_tokens = 8192 }
magi.casper = { tools = { dino = { off = true }, birdy = { hidden = true } } }
$ balthasar needs # what it will accept, in its own words
$ melchior needs
$ casper needs
A setting the far side does not recognise comes back named rather than as a generic refusal, because "refused" is not something a coordinator can act on. Its own vocabulary, not magi's: renaming a setting on one side shows up as a line on stderr instead of failing silently on the other.
configure sets something in the process that answers it — the whole of what a sibling needs when it is asked once and then runs for the session. casper is one process per call, so what magi decided rides on every spawn instead. Otherwise casper would report the setting taken and the next call would be a fresh process that had never heard of it.