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.
Global config
Loaded from Global.Path.config/nikcli.json and used for user-wide defaults.
Project config
Discovered upward from the working directory and worktree unless project config is disabled.
Config directories
Directories ending in .nikcli and NIKCLI_CONFIG_DIR can provide config, commands, agents, modes, plugins, and instructions.
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:
- Remote well-known config from saved auth records with type
wellknown. This is the lowest-precedence layer. - Global config from
Global.Path.config/nikcli.json. - Custom config file from
NIKCLI_CONFIG. - Project config files discovered upward, unless
NIKCLI_DISABLE_PROJECT_CONFIGis set. - Inline JSON from
NIKCLI_CONFIG_CONTENT. - Config directories from
ConfigPaths.directories, including global config dir, repo.nikclidirs, home.nikcli, andNIKCLI_CONFIG_DIR. - Command, agent, mode, and plugin entries loaded from those directories.
- Deprecated
modeentries are migrated intoagent. NIKCLI_PERMISSIONmerges intopermission.- Legacy top-level
toolsis converted into permission rules. - Runtime flag overrides such as
NIKCLI_DISABLE_AUTOCOMPACTandNIKCLI_DISABLE_PRUNEare applied.
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. |
NIKCLI_CONFIG=/path/to/nikcli.json nikcli
NIKCLI_CONFIG_DIR=/path/to/.nikcli nikcli
NIKCLI_DISABLE_PROJECT_CONFIG=1 nikcli 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.
{
"provider": {
"openai": {
"options": {
"apiKey": "{env:OPENAI_API_KEY}"
}
},
"openrouter": {
"options": {
"apiKey": "{file:./secrets/openrouter.txt}"
}
}
}
} 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
{
"$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 — 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.
{
"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
{
"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": {
"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.
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 for the full preset map and behavior table.
{
"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": {
"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 |