Navigation
Copy page

settings.json

On this page

settings.json configures kli's defaults: theme, key bindings, the model it starts on, where it writes session logs to, what its tools may do, which profile it boots, model option defaults, automatic compaction, and the observability sink. It is a single JSON object. Every key is optional.

kli reads two files and merges a profile overlay on top:

Layer Path Precedence
Global ~/.config/kli/settings.json lowest
Project <repo>/.kli/settings.json over global
Profile overlay the active profile's settings block over both

The project file is the nearest .kli/settings.json at or above the working directory, bounded by the repository root. The global path follows $XDG_CONFIG_HOME; ~/.config/kli/ is the default.

A kli image built with the programs.kli Nix module can carry one more layer below the global file: the keys baked in through programs.kli.settings. They act as image defaults, and the global and project files always override them.

Merge behavior

The layers deep-merge. For any key whose value is a JSON object, the higher layer's object merges into the lower layer's object key by key; for any other type the higher layer's value replaces the lower one. So a project file can override one nested key (a single keybindings entry, one defaultOptions entry, one observability field) without restating the rest, while a scalar like defaultModel is replaced outright.

The profile overlay applies last. Selecting a profile whose settings block sets a key wins over both files for that key. See Profiles.

Fail-soft

A settings file that does not exist, does not parse as JSON, or whose top level is not an object is reported as a warning and skipped; the other layers still load. A key whose value has the wrong type is reported and ignored, and kli keeps the default for that key. Malformed input never aborts startup. Warnings raised during boot surface in the session transcript rather than the terminal, because the UI takeover would otherwise erase them.

Keys

Key Type Default Effect
theme string "auto" Active color theme. "auto" resolves a light or dark theme from the terminal background. An unknown name is ignored with a warning.
keybindings object none Map of key id to action name. See Key bindings.
defaultProvider string none Model provider kli selects at startup. One of anthropic, openai, openai-codex, or a custom provider id from providers.json. Takes effect only with defaultModel.
defaultModel string none Model id within defaultProvider to select at startup. Takes effect only with defaultProvider. An unknown provider/model pair is ignored with a warning.
defaultOptions object none Semantic model option defaults applied to the selected default model. Unsupported or invalid options are ignored with warnings.
compaction object automatic compaction enabled at 85% Controls automatic session compaction. Disabling it does not disable manual /compact. See compaction.
sessionDir string session extension default Directory kli writes session logs to. A leading ~ expands to the home directory; a relative path resolves against the working directory.
capabilities array of strings none Capability names the agent's tools may exercise. See Capabilities.
trustRoots array of strings none (integrity only) Hex ed25519 public keys a remote install's signature must verify under. Empty checks the git pin only; setting keys requires a trusted signature. See trustRoots.
profile string interactive-terminal Profile kli boots. Overridden by --profile and KLI_PROFILE. See Profiles.
profiles object none User-defined profile specs, keyed by name. See Profiles.
observability object none JSONL event sink. See Observability.
extensions object none Per-extension settings subtrees, keyed by extension. A declared subtree validates against its extension's schema. See extensions.

theme

A theme name pins that theme. "auto", or the key absent, leaves kli to read the terminal background and pick a light or dark theme at boot. A name kli does not recognize is ignored with a warning, and the prior theme stands.

keybindings

An object mapping a key id to an action name:

json
{
  "keybindings": {
    "ctrl+z": "undo"
  }
}

Entries merge into the override layer over the built-in bindings. An action name kli does not recognize is skipped with a warning. A keybindings value that is not an object is ignored with a warning. The full action list is in Key bindings.

defaultProvider, defaultModel, defaultOptions

defaultProvider and defaultModel together select the model kli starts on. Both must be present and name a registered model; otherwise the selection is skipped and the warning names the rejected pair. Built-in provider ids are anthropic, openai, and openai-codex; custom OpenAI-compatible endpoints use the provider ids declared in ~/.config/kli/providers.json.

defaultOptions is a JSON object keyed by semantic model option id. It applies only when defaultProvider and defaultModel select a model, and each key must be supported by that model's option schema. Unsupported keys and invalid values are ignored with warnings. Accepted values are canonicalized before the session starts.

json
{
  "defaultProvider": "anthropic",
  "defaultModel": "claude-opus-4-8",
  "defaultOptions": {
    "reasoning-effort": "high"
  }
}

Built-in semantic option ids are documented in Providers and Transports. A model must declare an option schema before the option can be set. /thinking is the command UI for the reasoning-effort option, so a later /thinking command overrides defaultOptions.reasoning-effort for the rest of that session.

compaction

compaction is a JSON object that controls automatic session compaction after a model turn. It does not control manual compaction: /compact still works when compaction.enabled is false.

Field Type Default Effect
enabled boolean true Whether kli automatically compacts when usage reaches the threshold.
thresholdRatio number 0.85 Positive usage ratio of the selected model's context window that triggers automatic compaction after a turn.
reserveTokens integer derived from context window Positive token count to keep verbatim when compacting. When absent, kli derives a recent window from the model context size.
json
{
  "compaction": {
    "enabled": false,
    "thresholdRatio": 0.9,
    "reserveTokens": 30000
  }
}

Set only the fields you need; object merge behavior lets project settings override one field without restating the rest.

sessionDir

The directory kli writes its session log to. A leading ~ expands to the home directory; a relative path resolves against the working directory; an absolute path is used as written. When absent, the session extension's own default location applies.

capabilities

The authority every agent's tools run under. kli is fully permissioned by default: when the key is absent, the default subject applies and passes every capability check. There is no interactive approval prompt; restriction is configured here, up front.

The value is an array of capability name strings. A present array grants exactly those capabilities, transitively closed over their implications, and denies every gated operation outside that closure. An empty array denies all gated operations. A value that is not an array of strings is ignored with a warning, and the default subject applies.

json
{
  "capabilities": ["file/read"]
}

The capability names, the implication rules that let a coarse name like tools/standard grant finer ones, and the full table of gated operations are in Capabilities.

trustRoots

trustRoots opts in to signature verification for extensions installed from a URL with kli install or /install. It is an array of hex-encoded ed25519 public keys — the publishers you choose to trust.

When the array is absent or empty, a remote install verifies the integrity floor only: the fetched bytes must match their git-object pin, and anyone may publish. When the array holds one or more keys, kli additionally requires that the artifact carry a detached ed25519 signature — fetched from <url>.sig — that verifies under one of these keys over the exact bytes served. A missing signature, or one from a key you do not list, is refused rather than downgraded, and the check does not weaken when a later session restores the install.

json
{
  "trustRoots": [
    "3f8a1c2e9b7d4f60a51e8c2d9f0b4a7c6e1d3b850a51e8c2d9f0b4a7c6e1d3b85"
  ]
}

Each entry is the 64-character hex of a 32-byte ed25519 public key. A malformed or empty entry is dropped with a warning while the remaining keys stand, so one bad line never silently disables the ceiling; a trustRoots value that is not an array is ignored with a warning and leaves the integrity floor in place. For the publisher side — minting a key, signing an artifact, and handing out the public half — see Publishing extensions.

profile, profiles

profile names the profile kli boots. Resolution order is the --profile flag, then the KLI_PROFILE environment variable, then this key, then the default interactive-terminal. The other built-in profiles are headless, human-in-loop, and autonomous.

profiles declares user-defined profiles, an object keyed by profile name. Each entry may carry extends, enable, disable, and a settings overlay. A name that shadows a built-in profile is ignored with a warning, as is a malformed entry. Both keys are documented in Profiles.

observability

A nested object configuring a JSONL event sink:

Field Type Default Effect
path string none File the sink appends events to. A leading ~ expands to the home directory; a relative path resolves against the working directory. Absent or empty leaves the sink off.
events array of strings none Event-type name prefixes to record. A non-string member is dropped. Absent records every event.
json
{
  "observability": {
    "path": "~/.kli-events.jsonl",
    "events": ["tool", "model"]
  }
}

extensions

A top-level object whose keys are per-extension settings subtrees. Each installed extension may declare its subtree — the key names and value schemas it accepts — as a settings contribution. Declaring buys the extension the same treatment every built-in key gets:

  • Validation with boot diagnostics. When the extension activates, its subtree is checked against the declared schema, and every mismatch — a wrong type, a value outside its bounds, an enum member the schema does not list, a key the schema does not know — is reported as a warning naming the exact path (extensions.skar.cairn.granularity: expected one of "stage", "run", got "leaf"). Fail-soft as everywhere in settings: diagnostics never abort startup.
  • /settings introspection. The command lists each declared subtree, whether the files carry it, and its current diagnostics — plus any keys under extensions that no installed extension declares.
  • Declared defaults. A key the schema declares a default for reads as that default when the files omit it, so an extension's documented defaults hold without the user writing them out.
  • Tiering. The extensions object deep-merges like every other key: a project file overrides one nested key of one extension's subtree without restating the rest, and a profile overlay wins over both files.
json
{
  "extensions": {
    "skar": {
      "models": { "big": "anthropic/claude-opus-4-8" },
      "toolResultCap": 4096,
      "cairn": { "granularity": "run" }
    }
  }
}

Describes, never grants: no key under extensions confers authority. An extension's settings configure its defaults and sinks; what its tools may do is governed by capabilities alone.

Reload

The merged view re-reads both files when settings reload, and the active profile overlay re-merges on top of the fresh files. A profile switch reverts the standing overlay and applies the new one, so theme, keybindings, the default model, defaultOptions, compaction, sessionDir, and capabilities all follow the switch.