Settings and Where Configuration Comes From
Pi merges the agent-directory and project .pi settings files: scalars override, resource lists combine, and environment variables and CLI flags are documented per setting rather than as higher layers.
You edit settings.json, run /reload, and the setting does not take effect. Or it takes effect in a way you did not predict. Both come from the same cause: you were reasoning about one file when Pi merges several.
configuration.md gives the shape in one sentence. Pi supports user-level and project configuration. User-level lives in the agent directory, defaulting to ~/.pi/agent. Project configuration lives in .pi under the working directory and loads after project trust is granted — with one exception, sessionDir, which Pi reads before resolving trust so it can locate sessions.
The agent directory
Everything user-level sits here. PI_CODING_AGENT_DIR moves it, or the SDK’s agentDir option.
| Path | Responsibility |
|---|---|
<agent-dir>/settings.json |
Settings, resource paths, and Pi package declarations |
<agent-dir>/keybindings.json |
Custom terminal UI and application keybindings |
<agent-dir>/mcp.json |
MCP servers available in every project |
<agent-dir>/models.json |
Compatible endpoints, models, and model overrides |
<agent-dir>/auth.json |
Saved API keys and OAuth credentials |
<agent-dir>/AGENTS.override.md, AGENTS.md, AGENTS.MD, CLAUDE.md, CLAUDE.MD |
User instructions applied across working directories |
<agent-dir>/SYSTEM.md |
Replaces Pi’s default system prompt |
<agent-dir>/APPEND_SYSTEM.md |
Adds instructions to Pi’s system prompt |
<agent-dir>/extensions/, skills/, prompts/, themes/ |
User resources |
Note that the instruction filenames sit next to SYSTEM.md in the same directory but do different jobs. Instructions and the system prompt are separate surfaces, and chapter 18 treats them separately.
Settings merge, resource lists do not
settings.md opens with the rule that surprises people: project settings override agent-directory settings, and resource lists are combined.
Scalar settings follow ordinary override. Lists of resources — extensions, skills, prompts, themes, packages — are unioned from both levels, and that is where the filter syntax comes from:
| Entry | Meaning |
|---|---|
| a bare path | Load it |
+path |
Include one exact path |
-path |
Exclude one exact path |
!pattern |
Exclude glob matches |
The rule in one picture, including the list that behaves differently:
flowchart TD
K["A setting you want to change"] --> Q{"Which key is it?"}
Q -->|"a scalar"| S["Project value wins"]
Q -->|"a resource list"| L["Both files load, unioned"]
L --> L2["Dropping an entry removes nothing"]
L2 --> L3["Removal has to be explicit"]
Q -->|"defaultTools"| T["Plain names replace, + and - edit"]
Wrong: “the project file lists the resources it wants, and that is what loads.” Correct: resource lists from both files are unioned, so the project file selects nothing by omission. To stop something loading you name it with
-pathor!pattern.
defaultTools is the list that does not follow the union rule. settings.md is explicit about it: a list of only +name and -name entries changes the inherited selection, while a project list containing a plain name replaces it. Within one list, plain names form the selection and +name and -name then apply in order.
Paths resolve differently per scope: user resource paths resolve from the agent directory, project resource paths from the project .pi directory. Absolute paths and ~ work in both.
Three settings that only exist at one level
settings.md marks these in bold:
defaultProjectTrust— agent-directory settings only.httpProxy— agent-directory settings only.cacheWarming— global setting only.
A project file that tries to set defaultProjectTrust is not layering a preference; the setting is ignored there. That matters when you expect a repository’s .pi to decide its own trust posture.
What the environment changes
environment-variables.md groups what Pi reads from the environment. Only one of these changes where configuration is found:
| Variable | Description |
|---|---|
PI_CODING_AGENT_DIR |
Override the config directory; default is ~/.pi/agent |
PI_CODING_AGENT_SESSION_DIR |
Override session storage; overridden by --session-dir |
PI_PACKAGE_DIR |
Override the package directory, useful for Nix/Guix store paths |
PI_OFFLINE |
Disable automatic network activity, including model catalog refreshes |
PI_SKIP_VERSION_CHECK |
Disable the pi.dev latest-version request |
PI_TELEMETRY |
Override install/update telemetry with 1/true/yes or 0/false/no |
PI_CACHE_RETENTION |
Set to long for extended provider prompt caching where supported |
PI_SHARE_VIEWER_URL |
Override the base URL used by /share |
PI_RADIUS_GATEWAY |
Override the Radius gateway origin used by /bug uploads |
VISUAL, EDITOR |
External editor fallback when externalEditor is unset |
--offline is the command-line equivalent of PI_OFFLINE=1. Either way, environment variables apply for one process, which makes them the right tool for a temporary override and the wrong one for anything you want to keep:
PI_OFFLINE=1 pi --print "Summarize this repository"
PI_CODING_AGENT_DIR=/srv/pi pi --session-dir /srv/sessions
Put it in settings.json instead if it should persist.
Three files, and the overrides that reach past them
The file order itself is one sentence long, and its floor is the built-in default. .pi/settings.json joins it only after project trust is granted, which is chapter 8.
Environment variables and command-line options are not two more layers stacked on top. They are documented per setting, and they do not always win:
| Override | Documented against |
|---|---|
--session-dir |
PI_CODING_AGENT_SESSION_DIR and the sessionDir setting |
--tools, --no-tools, --no-builtin-tools |
defaultTools, including on reload |
--system-prompt |
Replaces the default system prompt |
--append-system-prompt |
Appends to it, and is repeatable |
--verbose |
quietStartup |
Where a capability has both a setting and an environment variable, the winner is stated for that capability, not globally: terminal-setup.md says settings take precedence over environment variables when overriding detected terminal features.
The difference from a stack is that there is nothing general to memorise about them: a handful of named settings, each with its own documented override. sessionDir is the one that bites, because Pi reads it before trust is granted — which is the next chapter.
A workable starting file
{
"compaction": {
"enabled": true,
"reserveTokens": 16384,
"keepRecentTokens": 20000
},
"defaultTools": ["+grep", "+find", "+ls"],
"shellPath": "C:\\Program Files\\Git\\bin\\bash.exe",
"shellCommandPrefix": "export CI=1",
"retry": {
"enabled": true,
"maxRetries": 3
},
"extensions": ["+./extensions", "!./extensions/experimental.ts"],
"defaultProjectTrust": "ask"
}
Two entries deserve a note. retry.provider.maxRetries defaults to 0 on purpose: settings.md says keep it there unless provider-level retries are required, because provider retries can delay Pi from handling quota and usage-limit errors itself. And defaultProjectTrust belongs in the agent directory, not in a project file.
Reload is not symmetric
Run /reload after manually changing settings, keybindings, instructions, or resources. What reload does not do matches what chapter 6 described: it enables newly added tools but does not disable removed ones. If a change seems inert, restart before you start debugging it.
Read settings instead of guessing
/settings opens the settings UI for common preferences. Beyond that, settings.md is the reference and pi config is the resource inspector: packages.md describes it as listing discovered resources and Pi’s built-in extensions, with --local to start from project overrides.
Everything so far has assumed the project settings file is one of the files Pi reads. It is not, until a question is answered. Chapter 8 is about that question, and about the resources a folder supplies that no amount of declining will switch off.