# Configuration

How nikcli discovers, parses, merges, validates, and applies config across global, project, and runtime sources.

## Overview

Config is resolved by `packages/nikcli/src/config/config.ts` with path discovery from `config/paths.ts`. Files are JSONC, support trailing commas, and are validated against `Config.Info`. Arrays for `plugin` and `instructions` are concatenated and deduplicated instead of simply replaced.

User

### Global config

Loaded from `Global.Path.config/nikcli.json` and used for user-wide defaults.

Repo

### Project config

Discovered upward from the working directory and worktree unless project config is disabled.

Plugins

### Config directories

Directories ending in `.nikcli` and `NIKCLI_CONFIG_DIR` can provide config, commands, agents, modes, plugins, and instructions.

Override

### Inline env config

`NIKCLI_CONFIG_CONTENT` is merged after global/custom/project config, before directory extras are loaded.

## Resolution order

The current source order is precise and matters:

1.  Remote well-known config from saved auth records with type `wellknown`. This is the lowest-precedence layer.
2.  Global config from `Global.Path.config/nikcli.json`.
3.  Custom config file from `NIKCLI_CONFIG`.
4.  Project config files discovered upward, unless `NIKCLI_DISABLE_PROJECT_CONFIG` is set.
5.  Inline JSON from `NIKCLI_CONFIG_CONTENT`.
6.  Config directories from `ConfigPaths.directories`, including global config dir, repo `.nikcli` dirs, home `.nikcli`, and `NIKCLI_CONFIG_DIR`.
7.  Command, agent, mode, and plugin entries loaded from those directories.
8.  Deprecated `mode` entries are migrated into `agent`.
9.  `NIKCLI_PERMISSION` merges into `permission`.
10.  Legacy top-level `tools` is converted into permission rules.
11.  Runtime flag overrides such as `NIKCLI_DISABLE_AUTOCOMPACT` and `NIKCLI_DISABLE_PRUNE` are applied.

Directory extras load after inline config

Even though `NIKCLI_CONFIG_CONTENT` is the highest file/content layer, later directory scans still load command, agent, mode, plugin, and `.nikcli/nikcli.json` entries from config directories.

## Where files live

| Location | How found | Purpose |
| --- | --- | --- |
| Global config dir | `Global.Path.config` | User-wide `nikcli.json`, command, agent, plugin, and skill files. |
| Project config | `Filesystem.findUp("nikcli.json")` | Repo or subdirectory-specific overrides. |
| `.nikcli` directories | `Filesystem.up` from directory/worktree and home | Local config modules and plugin entrypoints. |
| `NIKCLI_CONFIG` | Exact env path | Single custom config file. |
| `NIKCLI_CONFIG_DIR` | Exact env directory | Additional config directory, always included in directory scan. |

Terminal

bash 

```
NIKCLI_CONFIG=/path/to/nikcli.json nikcli
NIKCLI_CONFIG_DIR=/path/to/.nikcli nikcli
NIKCLI_DISABLE_PROJECT_CONFIG=1 nikcli
```

$ shell · paste into your terminal

## Config tokens

Config text supports `{env:VAR}` and `{file:path}` substitutions before JSONC parsing. Relative file paths resolve against the config file's directory. Lines commented with `//` skip file-token expansion.

nikcli.json

json 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

```
{
  "provider": {
    "openai": {
      "options": {
        "apiKey": "{env:OPENAI_API_KEY}"
      }
    },
    "openrouter": {
      "options": {
        "apiKey": "{file:./secrets/openrouter.txt}"
      }
    }
  }
}
```

Well-known config is safer

Remote well-known config only receives the saved token through an explicitly named env key from the auth record. It does not freely read process env or local file references.

## Minimal config

nikcli.json

json 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

```
{
  "$schema": "https://nikcli-ai.dev/config.json",
  "theme": "tokyonight",
  "model": "openai/gpt-5",
  "small_model": "openai/gpt-5-mini",
  "default_agent": "build",
  "share": "manual",
  "provider": {
    "openai": {
      "options": {
        "apiKey": "{env:OPENAI_API_KEY}"
      }
    }
  },
  "permission": {
    "bash": "ask",
    "edit": "ask",
    "webfetch": "ask"
  }
}
```

## Top-level fields

| Field | Purpose |
| --- | --- |
| `$schema` | Schema URL for editor validation. |
| `theme`, `locale`, `username` | Presentation, language, regional formatting, and displayed user label. |
| `model`, `small_model` | Default primary and helper model ids. |
| `default_agent`, `agent` | Default agent and agent-specific prompts/tools/mode config. |
| `provider` | Provider credentials, base URLs, models, variants, timeouts, allow/block lists. |
| `mcp`, `connectors` | External tool servers and service connectors. |
| `formatter`, `lsp` | Formatter and language server config or `false` to disable. |
| `permission`, `tools` | Current permission rules and legacy tool boolean compatibility. |
| `plugin`, `instructions` | Plugin specifiers and extra instruction files/patterns. Arrays concatenate across layers. |
| `server`, `remote` | Serve/web defaults and remote control behavior. |
| `share`, `autoshare` | Sharing mode. `autoshare` migrates to `share: auto`. |
| `analytics` | Anonymous usage reporting. On by default; see below to opt out. |
| `compaction` | Auto compaction, prune, and reserved token buffer. |
| `experimental` | Feature flags such as hooks, brain, memory, MCP timeout, native LLM runtime. |
| `image`, `attachment`, `speak` | Media generation and attachment normalization config. |
| `notifications`, `mobile` | Todo/general notifications and mobile development helpers. |

## Anonymous usage reporting

nikcli keeps per-day analytics locally, for the `/analytics` view. It also contributes those per-day totals, anonymously, to the public stats at [nikcli-ai.dev/data](/data) — which is how that page can show the models people actually run on their own provider keys rather than only what the nikcli gateway served. **This is on by default**, because a sample of only the people who opted in would measure those people rather than the community.

Turning it off, in order of precedence:

```
# either environment variable, no config file needed
DO_NOT_TRACK=1
NIKCLI_DISABLE_ANALYTICS=1
```

```
{
  "analytics": {
    "share": false
  }
}
```

The environment variables win over the config, so a machine or a CI image can opt out without a config file existing at all.

What is sent: date, provider, model, distinct session count, assistant message count, token counts and cost. Along with them goes a random UUID generated on your machine, so a resend replaces a day rather than doubling it — it is never published, and the public feed only ever emits a count of distinct installs.

What is never sent: prompts, responses, file paths, repository names, session titles, account details, or anything about your environment. Set `analytics.endpoint` to send reports to your own collector instead of nikcli's. A failed report is retried later and never surfaces an error. Run `nikcli analytics show` to see exactly what would be sent.

## Providers

Provider entries are partial models.dev providers plus nikcli-specific options. Use `enabled_providers` to allow only specific provider ids, or `disabled_providers` to remove automatic providers.

provider config

json 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

```
{
  "enabled_providers": ["openai", "anthropic", "openrouter"],
  "provider": {
    "anthropic": {
      "options": {
        "apiKey": "{env:ANTHROPIC_API_KEY}"
      }
    },
    "amazon-bedrock": {
      "options": {
        "region": "us-east-1",
        "profile": "default"
      }
    }
  }
}
```

## Agents and commands

`agent` config controls primary, subagent, and specialized agents. Deprecated `mode` entries are migrated into `agent` with `mode: primary`. Command templates are loaded from config and from config directories.

| Field | Purpose |
| --- | --- |
| `agent.build`, `agent.plan` | Primary code and plan agents. |
| `agent.general`, `agent.explore`, `agent.scout` | Subagent presets. |
| `agent.title`, `agent.summary`, `agent.compaction` | Specialized helper agents. |
| `command` | Named command templates available to CLI/TUI and prompt flows. |
| `instructions` | Additional instruction files or globs included in session context. |

## MCP and connectors

external tools

json 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

```
{
  "mcp": {
    "filesystem": {
      "type": "local",
      "command": ["bunx", "@modelcontextprotocol/server-filesystem", "."]
    },
    "remote-docs": {
      "type": "remote",
      "url": "https://example.com/mcp",
      "headers": { "Authorization": "Bearer token" }
    }
  },
  "connectors": {
    "github": { "type": "github", "enabled": true },
    "slack": { "type": "slack", "enabled": true },
    "figma": { "type": "figma", "enabled": true }
  }
}
```

| Config | Valid forms |
| --- | --- |
| `mcp.*` | Local stdio command, remote URL with headers/OAuth, or enabled-only toggle patch. |
| `connectors.*` | Figma, Slack, GitHub, Lovable, Discord, Teams, Google Chat, Linear, or enabled-only toggle patch. |

## LSP and formatter

`lsp` can be `false`, can disable a built-in server by id, or can define a custom server. Custom LSP servers must provide `extensions` unless they reuse a known built-in id. Formatter config follows the same pattern: disable all with `false`, override or disable named formatter entries otherwise.

lsp config

json 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

```
{
  "lsp": {
    "pyright": { "disabled": true },
    "custom-ts": {
      "command": ["typescript-language-server", "--stdio"],
      "extensions": [".ts", ".tsx"],
      "env": { "NODE_ENV": "development" }
    }
  },
  "formatter": {
    "prettier": {
      "command": ["bunx", "prettier", "--write"],
      "extensions": [".ts", ".tsx", ".json"]
    }
  }
}
```

## Permissions

Permission config accepts either a single action for all permissions, a tool-to-action map, or a tool-to-pattern map. Actions are `ask`, `allow`, and `deny`. The legacy top-level `tools` boolean map is converted into permission actions.

Permission modes

TUI and mobile expose three named presets that map to the same `permission` map shape shown below: `require_approval` (every tool asks), `approve_for_me` (the empty-map default — trusted local work is allowed, shell/network/external/sensitive actions ask), and `full_access` (every tool is allowed). Editing individual tools after a preset switches the value to `custom`. See [Permissions](/docs/permissions) for the full preset map and behavior table.

permission config

json 

1

2

3

4

5

6

7

8

9

10

11

12

```
{
  "permission": {
    "bash": {
      "git status*": "allow",
      "rm *": "deny",
      "*": "ask"
    },
    "edit": "ask",
    "webfetch": "ask",
    "lsp": "ask"
  }
}
```

## TUI and keybinds

| Field | Behavior |
| --- | --- |
| `tui.scroll_speed` | Fixed scroll speed when scroll acceleration is disabled. |
| `tui.scroll_acceleration.enabled` | Enable macOS-style scroll acceleration. |
| `tui.diff_style` | `auto` or `stacked` diff rendering. |
| `tui.mouse` | Enable or disable mouse capture. Default is true. |
| `tui.sound` | Enable ambient sound feedback. |
| `tui.bg_pulse` | Enable animated home-logo background pulse. |
| `keybinds.command_list` | Default `ctrl+p`. |
| `keybinds.agent_cycle` | Default `tab`. |
| `keybinds.input_submit` | Default `return`. |
| `keybinds.input_newline` | Default `shift+return,ctrl+return,alt+return,ctrl+j`. |
| `keybinds.session_parent` | Default `ctrl+b`, matching background/subagent parent behavior. |

## Notifications and mobile

Notification config is read by built-in notification plugins. Mobile config currently focuses on Tophat mobile project integration and defaults for platform/destination detection.

notifications and mobile

json 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

```
{
  "notifications": {
    "todo": {
      "enabled": true,
      "slack": { "enabled": true, "connector": "slack", "channel": "#builds" }
    },
    "notify": {
      "enabled": true,
      "events": { "sessionIdle": true, "permissionAsked": true },
      "rateLimit": { "windowMs": 60000, "maxPerWindow": 20 }
    }
  },
  "mobile": {
    "tophat": {
      "enabled": true,
      "defaultPlatform": "ios",
      "defaultDestination": "simulator"
    }
  }
}
```

## Environment flags

| Flag | Effect |
| --- | --- |
| `NIKCLI_CONFIG` | Load an explicit config file after global config. |
| `NIKCLI_CONFIG_CONTENT` | Merge inline JSON after project config. |
| `NIKCLI_CONFIG_DIR` | Add a config directory to directory scanning. |
| `NIKCLI_DISABLE_PROJECT_CONFIG` | Disable project-level file discovery and `.nikcli` project directories. |
| `NIKCLI_PERMISSION` | Inline permission overrides. |
| `NIKCLI_DISABLE_DEFAULT_PLUGINS` | Skip built-in external plugins. |
| `NIKCLI_DISABLE_EXTERNAL_SKILLS` | Disable external skill discovery. |
| `NIKCLI_DISABLE_LSP_DOWNLOAD` | Prevent automatic LSP server downloads. |
| `NIKCLI_ENABLE_EXPERIMENTAL_MODELS` | Keep alpha models in provider catalog. |
| `NIKCLI_DISABLE_AUTOCOMPACT` | Force `compaction.auto` false. |
| `NIKCLI_DISABLE_PRUNE` | Force `compaction.prune` false. |
| `NIKCLI_SERVER_PASSWORD` | Enable Basic Auth for server mode. |
| `NIKCLI_SERVER_TAILSCALE_AUTH` | Trust loopback Tailscale identity headers. |
| `NIKCLI_EXPERIMENTAL_HTTPAPI` | Enable typed HttpApi bridge. |
| `NIKCLI_EXPERIMENTAL_LSP_TOOL` | Enable the LSP tool in the registry. |
| `NIKCLI_EXPERIMENTAL_LSP_TY` | Use ty and disable pyright in the built-in LSP set. |

## Failure modes

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Config parse error includes JSONC input | Syntax error after token substitution. | Check the reported line/column and any `{file:*}` expansion. |
| Project config ignored | `NIKCLI_DISABLE_PROJECT_CONFIG` is set. | Unset the flag or use `NIKCLI_CONFIG`. |
| Custom LSP rejected | Unknown LSP id has no `extensions`. | Add the extension list or use a known built-in id. |
| Plugin duplicated | Multiple layers append plugin arrays. | Config loader deduplicates plugin specs, but check versioned specs and force install behavior. |
| Permission does not match | Later rules or stored always approvals override earlier ones. | Check merge order and project permission storage. |

## Source anchors

| Need | File |
| --- | --- |
| Config loading and schema | `packages/nikcli/src/config/config.ts` |
| Config path discovery | `packages/nikcli/src/config/paths.ts` |
| TUI config | `packages/nikcli/src/config/tui.ts`, `packages/nikcli/src/config/tui-schema.ts` |
| Environment flags | `@nikcli-ai/util/flag` |
| Global paths | `@nikcli-ai/util/global` |
| Plugin install config patching | `packages/nikcli/src/plugin/installer.ts` |
| Connector CLI config patching | `packages/nikcli/src/cli/cmd/connectors.ts` |

## Related

-   [CLI Reference Commands that read and write config.](/docs/cli)
-   [Providers Model and API provider keys in config.](/docs/providers)
-   [Permissions Permission rules in config overlays.](/docs/permissions)

---

Source: https://nikcli-ai.dev/docs/configuration
