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 combine
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 — combine entries from both levels rather than replacing one list with the other. Resource exclusions apply within the scope that declares them, as the tests at the end of this chapter demonstrate:
| 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, in the file that lists it. A filter in the project file does not reach a resource the user file lists.
defaultTools is the list that does not follow the union rule: plain names replace and +name and -name edit. That merge is chapter 6’s subject, and is not repeated here.
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. Several variables change where Pi finds configuration, sessions or packages; the others affect runtime behaviour:
| 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. An environment variable set for one command or shell session is useful for a temporary override. An environment variable configured persistently outside Pi can also apply to future processes, so its lifetime depends on how it is set:
PI_OFFLINE=1 pi --print "Summarize this repository"
PI_CODING_AGENT_DIR=/srv/pi pi --session-dir /srv/sessions
For a persistent option that Pi exposes in settings.json, prefer that file; for environment-only options, configure the environment in the process launcher or operating system.
Three files, and the overrides that reach past them
For ordinary settings, built-in defaults are the starting point, agent-directory settings provide the user layer, and trusted project settings override matching values. Pi does not load most project settings until project trust is granted; the early sessionDir lookup is the exception discussed in 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 |
--no-mcp |
extensions with -builtin:mcp, for one run rather than for good |
--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": {
"provider": {
"maxRetries": 0
}
},
"extensions": ["+./extensions", "!./extensions/experimental.ts"],
"defaultProjectTrust": "ask"
}
This example is for the agent-directory settings.json, not the project file: defaultProjectTrust is user-level only. The retry.provider.maxRetries setting is shown explicitly at its documented path with the default value 0; chapter 29 explains that choice. The shellPath example is Windows-specific and the shellCommandPrefix is Bash-specific, so adapt or omit those entries for your environment.
Reload is not symmetric
Run /reload after manually changing settings, keybindings, instructions, or resources. But reloading does not necessarily undo tool activations or deactivations already made in a running session; chapter 6 explains that asymmetry. If a changed tool setting does not take effect, restart Pi and inspect the resulting active tool set before assuming the configuration was ignored.
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.
What was run
Chapters 6 and 7 share examples/ch07-settings/, which runs the merge rules through Pi’s own SettingsManager and DefaultResourceLoader over real files:
- scalar settings: the project value wins, and a nested key the project does not set still comes from the user file;
- an untrusted project’s settings are not read at all, and granting trust afterwards makes the project layer apply;
defaultProjectTrustset in a project file is ignored: the agent-directory value stands;- resource lists from the two files are both loaded, and omitting an entry in the project file removes nothing.
It also found a limit the first draft of this chapter did not state. An exclusion filter applies in the file that lists the resource. A !pattern or -path in the user file removes the user’s skill. The same filter in the project file, tried three ways, left the user-level skill loaded, while a project filter did remove the project’s own entry. So a repository cannot switch off something you configured personally by naming it in its own .pi/settings.json. That is observed on 1.0.4 and is not stated either way in settings.md, which says only that resource arrays support the filters and that Pi loads resources listed in both files.