Migrating from Pi
radian config import-pi drafts a Radian configuration from a Pi agent directory. It is a
translation aid, not a compatibility layer:
Radian does not promise compatibility with Pi's file format. Pi may change its settings, models or keybindings at any time; the draft is a best-effort snapshot that you review and edit. Radian's own schema is the authority for what a Radian configuration means.
The command#
radian config import-pi --from <pi-agent-dir> # draft on stdout, report on stderr
radian config import-pi --from <pi-agent-dir> > draft.toml
radian config import-pi --from <pi-agent-dir> --write # save it, refusing to overwrite
- The Pi agent directory defaults to
~/.pi/agentfor Pi; for this command it is--from, which is required. There is no default, so the command never reads a directory you did not name. - It is its own mode: no
--model, no API key, no session file, no network. - Nothing is written without
--write.--writetargets the file--config/RADIAN_CONFIGnames, else~/.radian/config/config.toml, writes atomically (temporary file- rename) with mode
0600(directory0700), validates the draft with the real reader before and after the write, and refuses an existing file — this version has no faithful write-back, so it never rewrites a configuration it did not create.
- rename) with mode
What is read#
| Pi file | What is used |
|---|---|
<dir>/settings.json |
defaultProvider, defaultModel, theme, compaction.enabled |
<dir>/models.json |
the active provider's baseUrl, api, apiKey, headers, models[].id |
<dir>/keybindings.json |
the actions listed in the mapping table below |
A file that is not there is skipped and named in the report. A file that does not parse is an
error naming the file and the position (exit 1). mcp.json is named in the report but not
migrated (Radian's configuration has no MCP section in this version; the hosts take MCP servers
from --mcp-config).
Credentials are never migrated#
auth.json is never read — not opened, not even stated, so a credential file that cannot
be read (mode 0000) neither fails the migration nor leaks a byte. Saved credentials stay in
Pi; put the key in an environment variable instead.
| Pi value | What the draft gets |
|---|---|
"$NAME" / "${NAME}" |
api_key = { from = "env", name = "NAME" } |
| a literal value | api_key = { from = "env", name = "<PROVIDER>_API_KEY" } — the literal is not copied, and the report says a literal was skipped without showing it |
"!command" |
nothing; the report lists it and the command is never executed |
A baseUrl that carries credentials (https://user:pass@host/…) is a secret too, so it is not
migrated and the report says so. Header values follow the same rules: a literal, non-secret
value is carried over; environment interpolation, !command and secret-looking values are
skipped with a note (Radian's model.headers is a plain string map).
Mapping table#
| Pi | Radian | Notes |
|---|---|---|
settings.defaultProvider |
model.provider |
only for providers Radian has an adapter for (anthropic → anthropic; OpenAI-compatible names → openai-compatible); anything else is reported |
models.providers.<p>.api |
model.provider |
openai-completions/openai-responses/azure-openai-responses → openai-compatible; anthropic → anthropic |
models.providers.<p>.baseUrl |
model.base_url |
skipped when the URL carries credentials |
settings.defaultModel |
model.model |
else the first models[].id of the active provider |
models.providers.<p>.apiKey |
model.api_key |
as an environment reference (see above) |
models.providers.<p>.headers |
model.headers |
literal, non-secret values only |
settings.theme |
tui.theme |
dark/light/mono only; Pi's system has no Radian equivalent |
settings.compaction.enabled |
compaction.auto |
the token settings (reserveTokens, keepRecentTokens) have no Radian key |
keybindings.json actions |
tui.keybindings |
see below |
Pi actions → Radian actions (only same-meaning pairs): tui.input.submit → send,
tui.input.newLine → newline, tui.input.tab → complete, tui.editor.cursor* →
cursor_*/word_*/line_*, tui.editor.history* → history_*, tui.editor.delete* →
delete_*, tui.altScreen.page*/line*/top/bottom → scroll_*, tui.altScreen.search* →
find/find_next/find_prev/find_close.
Pi writes ctrl+shift+x; Radian writes ctrl-shift-x, so keys are translated. A migrated key
that is already another Radian action's default is dropped with a note (the TUI refuses to
start when one key is bound to two actions), and so is a duplicate inside the draft. Review the
tui.keybindings block: the TUI's own conflict check is the authority.
What is not migrated#
Anything without a Radian equivalent is listed in the report, by key name and never by value:
thinking levels, tool selection (defaultTools), session directories, retry/transport/network
settings, resources (packages, extensions, skills, prompts, themes), the shell settings,
project trust, MCP servers, model overrides, and any setting this version does not know.
Exit codes#
| Code | Meaning |
|---|---|
| 0 | the draft (or the write) succeeded |
| 1 | a missing directory, a malformed JSON file, a refused --write target, or an invalid draft |
| 2 | usage error (for example, no --from) |