Files and Paths
On this page
kli reads and writes files under two roots: a global directory shared across every project, and a per-project directory inside the repository you run in. A third set of paths under the cache directory holds diagnostic output. This page lists every path, what kli stores there, and how it is resolved.
Roots
| Root | Path | Resolution |
|---|---|---|
| Global config |
~/.config/kli/ |
$XDG_CONFIG_HOME/kli/; ~/.config/kli/ is the default. |
| Project config |
<repo>/.kli/ |
The nearest .kli/ directory at or above the working directory, bounded by the repository root (the directory holding .git). None outside a configured project. |
| Cache |
~/.cache/kli/ |
$XDG_CACHE_HOME/kli/; ~/.cache/kli/ is the default. Diagnostic sinks only. |
The project root is found by walking up from the working directory to the repository root. A .kli/ in an ancestor directory applies to every project under it; a .kli/ deeper down extends and shadows it.
Where both roots hold the same kind of file, the global file loads first and the project file layers over it. For settings the two deep-merge; for discovery directories the project entries extend and shadow the global set.
Configuration files
| Path | Purpose |
|---|---|
~/.config/kli/settings.json | Global settings: theme, key bindings, default model, session directory, capabilities, profile, observability. See settings.json. |
<repo>/.kli/settings.json | Project settings. Deep-merges over the global file, key by key. |
~/.config/kli/config.json | Extension control: which user extensions are enabled or disabled, and extra directories to discover extensions from. See config.json. |
~/.config/kli/providers.json | User-defined OpenAI-compatible providers, keyed by provider id. Holds endpoint and model definitions; never a key, only the name of an environment variable to read it from. See Connect a provider. |
A configuration file that is absent, does not parse as JSON, or whose top level is not the expected shape is reported as a warning and skipped. The other files still load. Malformed input never aborts startup.
Credentials
| Path | Purpose |
|---|---|
~/.config/kli/credentials.json | Stored provider credentials, keyed by provider id. Each record is a static API key or an OAuth token set (access, refresh, expiry, account id). |
kli writes this file with mode 0600 (owner read and write only). The write goes to a same-directory temporary file created under umask 077, then renames it over the target, so a crash mid-write cannot truncate the stored credentials and no world-readable window opens. The file lives in the global directory; there is no per-project credentials file.
Discovery directories
Each root holds optional subdirectories kli scans for resources. The global directory loads first, then the project directory, so project entries extend and shadow global ones by name.
| Subdirectory | Global path | Project path | Holds |
|---|---|---|---|
| Prompts |
~/.config/kli/prompts/ |
<repo>/.kli/prompts/ | Prompt templates. |
| Skills |
~/.config/kli/skills/ |
<repo>/.kli/skills/ |
Skills. Project skills also load root markdown files; ancestor .agents/skills/ directories and the built-in skills shipped with the image are scanned too. |
| Extensions |
~/.config/kli/extensions/ |
<repo>/.kli/extensions/ |
User extensions. A directory holding extension.lisp is one extension; an unmarked directory is a group whose files and subdirectories each contribute. See Extensions. |
Extra extension directories named under extension-dirs in config.json are scanned in addition to these, as is any path passed with --extension on the command line.
Context files
kli reads per-directory context files into the system prompt. In each directory it checks these names in order and takes the first that exists.
| Name | Role |
|---|---|
AGENTS.md | Per-directory project context. Checked first. |
CLAUDE.md |
Per-directory project context. Checked when no AGENTS.md is present. |
Context files are collected from the global directory first, then from each directory on the walk from the repository root down to the working directory. A file over 2 MiB is treated as absent.
Two further files in either config root replace or extend the system prompt rather than add project context. The project copy wins over the global one; a blank file is treated as absent.
| Name | Role |
|---|---|
SYSTEM.md | Replaces the base system prompt. |
APPEND_SYSTEM.md | Appended to the system prompt. |
Session logs
By default kli keeps the session log in memory. Set sessionDir in settings.json to a directory and kli persists each session to disk there.
| Path | Purpose |
|---|---|
<sessionDir>/<id>.session | One stored session. A readable record file: a versioned header followed by one record per session entry. The file name is the session id. |
sessionDir resolves a leading ~ against the home directory and a relative path against the working directory. Each rewrite goes through a same-directory .tmp file and a rename, so a crash mid-write cannot truncate a stored session. The .tmp files are not listed as sessions.
Cache: fault and fatal logs
kli writes diagnostics under the cache directory. These are append-only and exist only when something is written to them.
| Path | Purpose |
|---|---|
~/.cache/kli/<seam>-faults.log | Contained faults, one line each, for the named seam. A fault caught at an extension boundary is logged here instead of reaching the terminal. Each file is truncated when it passes a 1 MiB soft cap. |
~/.cache/kli/last-fatal.log | The condition and a backtrace for a fatal error that ended the process. Appended to on each fatal exit; the path is printed to standard error as it exits. |
The fault log file name carries the seam, so different boundaries write to different files (for example model-faults.log). A failure while writing a diagnostic loses that line and nothing else.
Observability sink
kli can append every dispatched event to a JSON Lines file. The sink is off until observability.path is set in settings.json.
| Setting | Effect |
|---|---|
observability.path |
File the sink appends to, one JSON object per line. A leading ~ expands against the home directory; a relative path resolves against the working directory. Absent or empty leaves the sink disabled. |
observability.events | Array of event-type name prefixes to record. Absent records every event. |
Each line carries the event timestamp, type, and an optional source and payload. A write failure is recorded on the sink and dropped, never breaking the action that emitted the event. The /observability command reports the sink's current path, filter, and write count.