Spirit
Download

Config

Data directories, config.json v1, .spirit/, and environment variables.

Shared user data follows the build quality, not the operating system's application-data directory.

Data directory

Override with SPIRIT_DATA_DIR. The server can also use SPIRIT_SERVER_DATA_DIR (wins over the former).

BuildmacOS / LinuxWindows
Stable~/.spirit%USERPROFILE%\.spirit
Dev~/.spirit-dev%USERPROFILE%\.spirit-dev

Breaking change: old platform data directories are not read or migrated automatically. Explicit data-directory overrides remain supported. Electron caches, browser storage, and window state remain in the private platform application directory.

Typical files:

{spiritDataDir}/
  config.json
  mcp.json
  hooks.json
  rule.md
  skills/
  chats/
  plans/
  extensions/
  automations/
  dreams/
  server.token
  server/instances/

CLI binaries use SPIRIT_HOME. On Unix, the Stable install and shared data may have the same parent directory; bin/ and cli/ remain installation files. The installer setting does not select the shared-data directory.

config.json v1

schemaVersion must be 1. Any other value is rejected — delete the old file and reconnect a provider. There is no migration from older shapes.

Core fields:

  • providers — connected providers: endpoint, transport, credentials, models
  • activeModel
  • imageGenerationModel / videoGenerationModel / secondaryLanguageModel
  • agents — agent toggles such as agents.providerTools.enabled
  • permission — permission allowlist rules

activeModel and the other model slots are model refs: { "providerId": "…", "name": "…" }, where providerId is a providers[].id and name is one of its models[].name.

Provider entries

Each providers[] entry is a fully declarative connection — what the connect wizard writes is what the runtime reads. The runtime never re-derives apiBase from the provider's identity; site, plan, and cloud choices are baked into the final URL at connect time.

Connection-level fields:

  • id (required) — unique entry id. Model refs and the internal keyring account point at it
  • label — optional display name
  • apiBase (required) — the endpoint, the single source of truth
  • transportKind — wire protocol: openai-compatible (default) / open-responses / anthropic / bedrock
  • sdk — full npm package name of the bundled SDK adapter (for example @ai-sdk/openai-compatible). Absent = the transport default; an unknown package name is rejected at load
  • headers — literal extra request headers merged into every outbound request
  • body — literal fields merged into every request body. An escape hatch for odd wire quirks — prefer a structured declaration where one exists
  • providerTools — tools the provider exposes (web search, code interpreter). See Provider Tools
  • fileUpload — the upload channel for local files that should not go inline (path / url, purpose, idPath, refTemplate, maxBytes). Absent = media go inline
  • imageApi / videoApi — API shape names for image / video generation; absent = no such generation
  • options — transport parameters; the legal key set depends on transportKind × sdk (awsRegion for bedrock, project / location for @ai-sdk/google-vertex, auth / storedState / applyPatch switches on open-responses). Unknown keys are rejected
  • models (required array, may be empty)

Model-level fields — capabilities, contextLength, outputLimit, reasoningEffort, supportedReasoningEfforts, reasoningMode, supportedReasoningModes, supportsThinkingType, supportsThinkingSwitch, thinkingSwitch, serviceTiers, serviceTier — may also be declared flat on the provider entry as defaults inherited by every model; a model's own declaration wins. Inside models[], everything except name is optional, and absent means undeclared, not a default.

  • capabilities tokens: languageModel, image, audio, video, imageGeneration, videoGeneration
  • outputLimit is the output token cap, separate from contextLength
  • supportedReasoningEfforts / supportedReasoningModes / serviceTiers are the sole picker and validation sources — an out-of-list selection is a load-time error, never silently clamped
  • thinkingEnabled is your thinking on/off state (not a capability); it is model-level only and requires supportsThinkingSwitch declared

Key channels

Keys do not have to live in this file — the connect wizard writes them to the keyring by default. Config may instead declare a channel, resolved in this order:

  1. apiKey — plaintext. Not recommended; an escape hatch only
  2. apiKeyEnv — the name of an environment variable to read the key from
  3. apiKeyKeyring — { "service": "…", "account": "…" }, an entry in an external keyring
  4. Internal keyring — the implicit fallback written by the wizard (account provider::{id})

Composite credentials (Bedrock IAM, Vertex service accounts) use a credentials slot map instead of the flat keys:

{
  "credentials": {
    "accessKeyId": { "env": "AWS_ACCESS_KEY_ID" },
    "secretAccessKey": { "keyring": { "service": "…", "account": "…" } }
  }
}

Slot names are protocol fields — apiKey, accessKeyId / secretAccessKey (Bedrock IAM / SigV4), clientEmail / privateKey (Vertex) — and unknown slots are rejected. Each slot resolves value → env → keyring → internal keyring (provider::{id}::{slot}; the apiKey slot is plain provider::{id}). Flat keys and credentials.apiKey cannot coexist in one entry.

agents.providerTools

agents.providerTools.enabled gates all provider-declared tools at once (renamed from agents.webSearch.enabled; the semantics did not change: opt-out, on by default). Set to false, no provider tool entries or body switches reach requests.

Catalog refresh and reconnect

Routine catalog refreshes rewrite the model declaration fields the catalog owns (capabilities, supportedReasoningEfforts, supportedReasoningModes, serviceTiers, …) — model facts come from the catalog as the single source of truth, so manual edits to those fields are overwritten on the next refresh. Provider-level materializations (apiBase, sdk, providerTools, fileUpload, options, imageApi / videoApi) change only when you reconnect the provider. Reconnect resets the wizard-owned keys (edits you made to them are overwritten) and preserves everything else: label, credential channels, headers / body, flat model defaults, and models you added by hand.

permission

permission pins down what individual shell commands and file paths may do, on top of the session approval level. Keys are tool names (shell, read_file); values map wildcard patterns to allow, ask, or deny.

{
  "permission": {
    "shell": {
      "git *": "allow",
      "rm -rf *": "deny"
    }
  }
}

deny is absolute — no approval level, hook, or session memory overrides it. Pattern syntax, path semantics, and the full action list are in Approvals. Verify rules without running anything: spirit permission check --shell "git status".

The old tool-permissions.json (written by the "Always Trust" button) is deprecated: the code no longer reads or writes it, and there is no automatic migration. Leftover files are inert — delete them manually.

Workspace .spirit/

PathRole
.spirit/rule.mdWorkspace rule
.spirit/skills/Workspace skills
.spirit/mcp.jsonWorkspace MCP
.spirit/hooks.jsonWorkspace hooks

Also used: {workspace}/AGENTS.md, {workspace}/.agents/skills/, {workspace}/plans/.

Environment variables

VariableRole
SPIRIT_DATA_DIRData directory
SPIRIT_SERVER_DATA_DIRServer data directory (wins)
SPIRIT_ACP_DATA_DIR / SPIRIT_ACP_WORKSPACEACP
SPIRIT_HOME / SPIRIT_VERSION / SPIRIT_DOWNLOAD_HOSTCLI installer
SPIRIT_API_KEY / SPIRIT_API_BASECLI process overrides
SPIRIT_WEB_HOST / SPIRIT_WEB_PORT / SPIRIT_DESKTOP_WEB_HOSTDesktop Web Host
SPIRIT_TERMINAL_SHELLDesktop PTY shell