Configuration reference
This is the full configuration reference. For a friendlier overview, see Settings.
Every key you can meaningfully set is documented below. Your config.json also carries a handful of keys the app writes for itself and never surfaces in the UI, such as remembered window bounds and pop-out positions, saved workspace layouts, which announcements you have dismissed, and the model lists discovered per agent. They are safe to leave alone, and deleting one just makes the app re-derive it.
Configuration cascade
Section titled “Configuration cascade”Kangentic resolves settings in three layers:
- Built-in defaults - sensible defaults for all settings
- Global user config - your personal defaults for all projects
- Project overrides - per-repository settings
Effective config = deep-merge(defaults, global config, project config).
New project seeding
Section titled “New project seeding”When you create a new project, Kangentic seeds its .kangentic/config.json with only a subset of your most recently configured project’s settings: the Theme tab’s four keys (theme, themeFollowsSystem, themeLight, themeDark), agent.permissionMode, and git.*. Everything else falls back to the built-in defaults. (terminal.* used to be seeded here; it became global-only in v0.31.0 and is no longer per-project.) Project-specific data - notably browser.* (including browser.defaultUrl) and saved backlog import sources - is stored per-project and is never copied into a new project, so one repository’s dev-server URL or issue-tracker connections cannot leak into another.
Configuration files
Section titled “Configuration files”Global: %APPDATA%\kangentic\config.json
Project: <project>/.kangentic/config.json
Global: ~/Library/Application Support/kangentic/config.json
Project: <project>/.kangentic/config.json
Global: ~/.config/kangentic/config.json
Project: <project>/.kangentic/config.json
Project settings override global settings when both are present.
A settings write that cannot reach disk (a data directory on a removable volume that has gone away, say) never crashes the app. Since v0.42.0 it raises one toast per failing file, and stays quiet until a later write to that file succeeds. The two files degrade differently: a global setting keeps applying for the rest of the session even though it was not saved, while a project setting is lost, since the project file is read back on demand and still holds its old value.
Two things changed in v0.43.0. The per-file toast now names the cause when the operating system identifies one: a full disk, a missing permission, a read-only volume, or a drive that is not there. An error code with no mapped cause still falls back to the plain “could not write to its data folder” sentence rather than guessing.
And a setting you just changed raises its own This setting did not save toast when that particular write does not land. The per-file notice alone was not enough: its busiest writer is the window-bounds save, so it was usually spent on a window move, and every settings change after that would have been silent for the rest of the session. The second toast is bucketed per setting rather than per file, on a 60-second cooldown, so one gesture that commits several times (a number field commits on every keystroke) says it once while a different setting failing moments later still gets its own.
Application settings
Section titled “Application settings”The keys behind the Board, Task, Changes, and Terminal tabs and the two dashboards.
Top-level settings
Section titled “Top-level settings”| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
theme | string | "dark" | Both | The hand-picked UI theme, painted while themeFollowsSystem is off: dark (Graphite), light (Paper), rust, clay, moon, forest, ocean, ember, sand, mint, sky, peach |
themeFollowsSystem | boolean | false | Both | Paint themeDark while the OS appearance is dark and themeLight while it is light, ignoring theme. The OS reading is never written; the app resolves it live |
themeLight | string | "light" | Both | The theme for a light OS appearance when following the system. A dark-base value here falls back to light |
themeDark | string | "dark" | Both | The theme for a dark OS appearance when following the system. A light-base value here falls back to dark |
sidebarVisible | boolean | true | Global only | Show/hide the sidebar |
boardLayout | string | "horizontal" | Global only | Reserved; nothing reads it and the Board tab offers no control for it. The board scrolls sideways |
cardDensity | string | "default" | Global only | Task card density: compact, default, or comfortable |
cardPreview | string | "agent-latest-message" | Global only | What a board card (and an Agent Monitor card) prints under its title: agent-latest-message (the agent’s newest message, wrapped to the slot), agent-messages (the newest messages one line each, newest last), or description. The two agent options fall back to the description while a task has no session, while its session is not running, or while its agent has not said anything yet. Labeled Card preview in Settings > Task |
columnWidth | string | "default" | Global only | Column width: narrow, default, or wide |
showTaskNumbers | boolean | true | Global only | Show each task’s #N (display ID) number on its board card. Labeled Ticket numbers in Settings > Task |
terminalPanelVisible | boolean | true | Global only | Show the bottom terminal panel |
statusBarVisible | boolean | true | Global only | Show the status bar at the bottom of the window |
animationsEnabled | boolean | true | Global only | Enable transition animations across the UI. Edited under Settings > Performance since v0.43.0, not Board: it sets a class on the document root, so it was never board chrome |
graphicsAccelerationEnabled | boolean | true | Global only | Chromium hardware rendering for the app window and the terminals, under Settings > Performance. Off starts the app with --disable-gpu and --in-process-gpu, which puts terminals on the DOM renderer and takes the graphics process off the table. Kangentic sets it to false by itself, once, on the launch after a run the graphics process killed, and never sets it back to true. Read before the window opens, so a change needs a restart |
graphicsAccelerationOffBy | string | null | null | Global only | Who last turned graphicsAccelerationEnabled off: "app" or "user", null while it is on. It is what keeps a later graphics failure from overwriting a choice you made, and what decides whether the Performance tab shows the callout explaining the downgrade |
diffViewMode | string | "split" | Global only | Default layout for Git file diffs in the Changes panel: split (side by side) or inline (unified) |
diffDefaultScope | string | "working" | Global only | Scope a freshly opened Changes panel shows: working (uncommitted edits), staged (index), or branch (the full branch vs its base) |
diffIgnoreWhitespace | boolean | false | Global only | Hide whitespace-only changes in the diff to filter out reformatting noise |
diffCollapseUnchanged | boolean | false | Global only | Fold away large unchanged regions, showing only the changed hunks with a little context |
diffWrapLines | boolean | false | Global only | Soft-wrap long lines onto the next row instead of scrolling the diff horizontally. Applies to both split and inline mode |
diffUseInlineWhenNarrow | boolean | true | Global only | Render a narrow diff pane inline (unified) even when split is selected, instead of squeezing two columns. Off forces side by side at any pane width |
diffFileSort | string | "name" | Global only | Changes panel file ordering: name, status (additions first, deletions last), size (most-changed first), or ext (by extension). In the flat list, status adds a heading and count at each group boundary |
diffFlatList | boolean | false | Global only | Show changed files as a flat list of full paths instead of a nested directory tree |
windowLightDismiss | string | "focused" | Global only | Whether clicking empty space outside a task-detail window dismisses windows: off (never), single (only when exactly one window is open), focused (the focused window, whichever is open), or all (every open window). Dismissing keeps the agent running and returns its terminal to the bottom panel. A persisted single migrates to focused once on upgrade |
skipDeleteConfirm | boolean | false | Global only | Skip confirmation dialog on task delete |
skipBoardConfigConfirm | boolean | false | Global only | Skip confirmation when applying board config changes |
autoFocusIdleSession | boolean | false | Global only | Auto-switch to session tab when agent goes idle |
stopLeftoverProcesses | boolean | true | Global only | Stop what a task’s agent left running in the task’s folder when the task reaches Done or To Do, or is deleted, and what a Command Terminal’s agent left running in its folder when the terminal ends. A process is stopped only when it carries the task’s tag, works inside the task’s worktree or project folder, and is not shared. Off, nothing is stopped and a toast still lists what kept running. See Worktree lifecycle |
autoNameRateLimitPerHour | number | 60 | Global only | Maximum naming calls per rolling 60-minute window, task titles and Command Terminal names combined, capping cost on burst task creation. Both run on the Automation agent (automation.agent) since v0.46.0, and with none chosen nothing is named. 0 disables the limit. Config-file only (no Settings toggle) |
restoreWindowPosition | boolean | true | Global only | Restore window position and size from last session |
hotkeyOverrides | Record<string, string> | {} | Global only | Rebound hotkeys, mapping a keybinding action id such as commandBar.toggle to a combo. A combo is either a keyboard chord (Mod+Shift+K, where Mod is Cmd on macOS and Ctrl elsewhere) or a mouse button (Mouse:Middle, Mouse:Back, Mouse:Forward). Absent ids use the registry default. Edited in the Hotkeys tab and replaced wholesale on save, so a reset removes the key. See Hotkeys |
statusBarPeriod | string | "live" | Global only | Deprecated: the status-bar usage strip was replaced by the Usage Stats in v0.30.0. Read once as a seed fallback for usageStatsPeriod, never written anymore |
Usage Stats settings
Section titled “Usage Stats settings”Persistence for the Usage Stats. Both values are one global selection shared across all projects.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
usageStatsPeriod | string | "live" | Global only | Saved time range for the usage dashboard: live, today, week, month, all |
usageStatsScope | string | "project" | Global only | Saved dashboard scope: project (current project) or all (app-wide rollup) |
Agent Monitor settings
Section titled “Agent Monitor settings”Persisted view preferences for the Agent Monitor, shared by the in-app overlay and the pop-out window.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
monitor.layout | string | "cards" | Global only | Monitor layout: cards, table, or list |
monitor.groupBy | string | "project" | Global only | Grouping: project or state |
monitor.sort | string | "longest-running" | Global only | Row order within a group: longest-running or recently-started |
monitor.liveOnly | boolean | false | Global only | Show only sessions with a live agent (the Idle and Active buckets), hiding Paused - which covers both suspended and queued sessions - and Recently finished. It deliberately never hides Idle: those are agents waiting on you |
monitor.textFilter | string | "" | Global only | Persisted toolbar filter text |
monitor.projectFilter | string[] | [] | Global only | Project ids the monitor is scoped to; empty means every project. Written by the toolbar’s Projects multi-select and kept across restarts (sanitized to a deduped list). An id naming a project with no sessions in the next non-empty snapshot is dropped and re-saved, so the filter always stays representable in the control |
monitor.stateFilter | string[] | [] | Global only | Restrict the monitor to these session states: needs-you, working, idle, finished. The values map to the monitor’s section labels Idle, Active, Paused, and Recently finished, in that order. No control writes it, and the monitor resets it to [] every time it loads the config, so a value written into config.json by hand never takes effect. The key exists so a filter persisted by an older build cannot hide rows that no control could bring back |
Backlog settings
Section titled “Backlog settings”| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
backlog.priorities | array | See below | Both | Customizable priority levels with labels and colors |
backlog.labelColors | object | {} | Both | Default color mappings for backlog labels ({ "label-name": "#hex" }) |
Default priorities:
| Index | Label | Color |
|---|---|---|
| 0 | None | #6b7280 |
| 1 | Low | #3b82f6 |
| 2 | Medium | #eab308 |
| 3 | High | #f97316 |
| 4 | Urgent | #ef4444 |
Terminal settings
Section titled “Terminal settings”| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
terminal.shell | string | null | null | Global only | Shell executable path. null = auto-detect |
terminal.fontFamily | string | "Menlo, Consolas, \"Courier New\", monospace" | Global only | Terminal font family. The Settings picker is a system-font autocomplete populated from your installed fonts |
terminal.fontSize | number | 14 | Global only | Terminal font size (px), from 8 to 32 |
terminal.cursorStyle | string | "block" | Global only | Cursor style: block, underline, or bar |
terminal.backspaceSendsCtrlH | boolean | false | Global only | Labeled Word delete on Backspace: plain Backspace sends Ctrl+H (0x08) instead of Delete (0x7f), so Claude Code’s TUI deletes the previous word instead of one character. Off by default; Ctrl+W, Alt+Backspace, and Ctrl+Backspace already word-delete regardless |
terminal.colors | TerminalColorOverrides | {} | Global only | Custom terminal background, foreground, and cursor color, set via swatches in the Terminal tab’s Colors section. Unset slots fall back to the built-in defaults (background #0c0c0c, foreground/cursor #e4e4e7). The 16-color ANSI palette (Windows Terminal “Campbell”) is a fixed built-in scheme, not individually editable |
terminal.showPreview | boolean | false | Global only | Reserved; nothing reads it. What a card shows under its title is cardPreview above |
terminal.panelHeight | number | 250 | Global only | Bottom panel height (px) |
terminal.panelCollapsed | boolean | false | Global only | Persisted collapsed state of the bottom terminal panel |
Agent and git settings
Section titled “Agent and git settings”Which CLI a column starts, how it is allowed to act, and how worktrees, branches, and pull requests behave.
Agent settings
Section titled “Agent settings”Agent settings are unified across all fourteen coding agents: Claude Code, Codex CLI, Gemini CLI, Cursor CLI, GitHub Copilot CLI, Grok Build, Antigravity CLI, Goose CLI, OpenCode, Aider, Qwen Code, Kimi Code, Factory Droid, and Warp/Oz CLI. The local-chat Ollama adapter is also detected and can take a CLI-path override (it appears in agent.cliPaths below), but it has no model/effort settings of its own and its one permission entry, Chat, is informational: no flag reaches the CLI.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
agent.permissionMode | string | "acceptEdits" | Both | Default permission mode for spawned agents |
agent.cliPaths | object | {} | Global only | Per-agent CLI path overrides, keyed by agent name (claude, codex, gemini, cursor, copilot, grok, antigravity, goose, opencode, aider, qwen, kimi, droid, warp, ollama). null = auto-detect. Example: { "claude": null, "codex": "/usr/local/bin/codex", "kimi": "/Users/me/.local/bin/kimi" } |
agent.launchOptions | object | {} | Global only | Per-agent boolean startup toggles (agent name to option id to enabled), shown as Launch options rows in the Agent tab when the selected agent declares one. Today only Codex declares one, Disable ChatGPT Apps (disableApps), which launches with --disable apps to skip the optional cloud ChatGPT Apps connector that can hang startup |
agent.executionServers | object | {} | Global only | Remote-server identity for agents that support remote execution (today OpenCode), keyed by agent name: { url, auth }, where auth is none, HTTP basic, or a bearer token read from an env var. Machine-scoped like agent.cliPaths - it names a server, not a project |
agent.execution | object | {} | Both | Per-agent, per-project execution mode: { mode: "local" | "remote", workingDirectory }. An absent entry means local. workingDirectory is a path on the server for this project’s tasks. Stored per-project and never seeded into new projects |
agent.maxConcurrentSessions | number | 8 | Global only | Max concurrent PTY sessions across all agents |
agent.queueOverflow | string | "queue" | Global only | Stored and shown on the Behavior tab as When max sessions are reached, but read by nothing: every spawn past the limit queues, whichever value is set |
agent.idleTimeoutMinutes | number | 0 | Global only | Auto-suspend sessions after N minutes idle. 0 = disabled |
agent.autoResumeSessionsOnRestart | boolean | true | Global only | When true, agent sessions that were running at last close auto-resume when Kangentic restarts. When false, sessions stay paused and require a manual Resume click on each task. Turn off if auto-resuming many agents at once overwhelms your machine. |
Permission modes
Section titled “Permission modes”Each agent advertises its own supported permission modes, and the Column Manager and Settings dropdowns update dynamically when you switch agents. The full set of modes is:
| Mode | Behavior | Supported by |
|---|---|---|
plan | Read-only tools auto-approved; edits require approval | Claude, Codex, Gemini, Copilot, Grok, Antigravity, Goose (chat only, no file access), OpenCode (Plan agent), Aider, Qwen, Kimi, Warp |
default | Uses the agent’s built-in defaults (e.g., Claude Code’s .claude/settings.json allowlist) | Claude, Codex, Gemini, Cursor, Copilot, Grok, Antigravity, Goose, Aider, Qwen, Kimi, Droid, Warp, Ollama (a single informational Chat entry; no flag is injected) |
acceptEdits | File edits auto-approved; other actions prompt (OpenCode: maps to its Build agent) | Claude, Codex, Gemini, Copilot, Grok, Antigravity, OpenCode, Aider, Qwen |
bypassPermissions | Full autonomy, no permission prompts at all | Claude, Codex, Gemini, Cursor, Copilot, Grok, Antigravity, Goose, Aider, Qwen, Kimi, Warp |
dontAsk | Deny everything not already allowed, no interactive prompts | Claude, Codex, Copilot, Grok |
auto | Classifier-driven auto-approval | Claude, Copilot, Grok |
Per-agent defaults:
| Agent | Default permission mode | Notes |
|---|---|---|
| Claude Code | acceptEdits | |
| Codex CLI | acceptEdits | The dropdown labels this preset Auto (Preset), after Codex’s own naming. It is acceptEdits, not the auto row above, which Codex does not offer |
| Gemini CLI | acceptEdits | |
| Cursor CLI | bypassPermissions | Non-interactive mode is the default so the stream-JSON init event fires and the ContextBar model pill resolves |
| GitHub Copilot CLI | acceptEdits | |
| Grok Build | acceptEdits | All six Kangentic modes pass through 1:1 via --permission-mode; labels describe grok’s own behavior |
| Antigravity CLI | acceptEdits | Mapped onto agy’s autonomy flags (--mode plan, --mode accept-edits, --dangerously-skip-permissions); default is agy’s request-review mode |
| Goose CLI | default | Modes map onto Goose’s GOOSE_MODE env var (chat, smart_approve, auto). Accept Edits is not offered: Goose has no mode that auto-approves edits while still gating shell commands, so it resolves down to Smart Approve |
| OpenCode | acceptEdits | Labels surface OpenCode’s native Plan and Build agents - selecting Plan maps to OpenCode’s read-only built-in agent |
| Aider | bypassPermissions | Aider is an edit-first CLI that expects broad file-write access |
| Qwen Code | acceptEdits | Labeled “Auto Edit” - soft fork of Gemini CLI |
| Kimi Code | default | Labels: Plan, Default (Confirm Actions), YOLO |
| Factory Droid | default | Single “Default” entry; autonomy is controlled in the Droid TUI (Shift+Tab cycles low/medium/high) |
| Warp (Oz CLI) | default | Permission-mode labels are informational only; Warp manages permissions via agent profiles (--profile) |
| Ollama | default | A single informational Chat entry; no flag is injected |
Permission mode is resolved in priority order:
- A column whose
permission_modeisplan. A read-only planning phase is a safety guarantee, so it beats every other rung, including a task’s own pin - Task’s
permission_mode(if set) - pinned in the New Task dialog’s Advanced section or the task-detail edit form; it wins for the task’s whole lifetime, and column moves cannot change it, other than theplancolumn above - Column’s
permission_mode(if set) agent.permissionModefrom config
Git settings
Section titled “Git settings”| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
git.worktreesEnabled | boolean | true | Both | Give tasks the option to run in their own git worktree. Off, the New Task dialog drops the Worktree or Project choice and tasks run in the project folder; a task that already has a worktree keeps it |
git.autoCleanup | boolean | true | Both | Delete branches when worktrees are removed |
git.defaultBaseBranch | string | "main" | Both | Default base branch tasks start from: both the ref a worktree is cut from and the one a non-worktree task’s branch checkout is fast-forwarded from |
git.copyFiles | string[] | [] | Both | Files to copy from repo root into new worktrees |
git.initScript | string | null | null | Both | Shell script run in each new worktree after creation (e.g. npm install). Runs to completion before the agent spawns; a non-zero exit fails worktree setup |
git.linkNodeModules | boolean | true | Both | Symlink the repo-root node_modules into each new worktree so agents skip a fresh install. Disable to let git.initScript install dependencies in the worktree itself |
git.prAutoRefresh | boolean | true | Both | Keep linked pull-request state and merge readiness current from a per-PR queue: each open PR is checked about 2 minutes after its own last check, one call at a time and at least 10 seconds apart. false leaves the check when the project opens and turns off the 30-second re-check of a PR whose checks are in flight. Replaces git.prRefreshIntervalMinutes, which the first v0.44.0 launch reads once |
git.autoFetch | boolean | true | Both | Run a background git fetch --all --prune of the open project’s remotes 5 minutes after its last full fetch, so ahead/behind counts and base-drift checks read current remote refs without anyone opening a panel. Fetch only - never a pull, merge, or rebase - and credential prompts are disabled for it. false stops the sweep; the fetch on project open still runs, and creating a worktree always fetches its base. Replaces git.autoFetchIntervalMinutes, which the first v0.44.0 launch reads once |
git.prBypassCountsAsReady | boolean | true | Both | On GitHub, count your own merge bypass as ready: a PR still waiting on a required review reads blocked even when your branch-protection bypass would merge it, and stays that way once a sibling PR lands and leaves it behind the base. On, such a PR reads ready, at one extra gh call per such PR per check - never past a check, since every check the base requires must have reported green. Turn it off to keep the review norm even where you could bypass. Azure DevOps ignores this; since v0.44.0 an Azure PR always has its branch policies evaluated, and git.prEvaluateBranchPolicies is retired |
Surfaces
Section titled “Surfaces”The context bar, the project sidebar, the embedded browser, the Agent Browser tools, and the dev-server port range.
Context bar settings
Section titled “Context bar settings”| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
contextBar.showShell | boolean | true | Global only | Show shell name in context bar |
contextBar.showVersion | boolean | true | Global only | Show agent CLI version |
contextBar.showElapsed | boolean | true | Global only | Show a ticking wall-clock elapsed time since the session started |
contextBar.showCost | boolean | true | Global only | Show session cost |
contextBar.showToolCalls | boolean | true | Global only | Show the cumulative count of completed tool calls. Click the pill for a per-tool breakdown |
contextBar.showAgentActive | boolean | false | Global only | Show the agent’s active time as reported by the CLI. Opt-in by default (it overlaps conceptually with elapsed time) to keep the bar lean |
contextBar.showTokens | boolean | true | Global only | Show token usage |
contextBar.showContextFraction | boolean | true | Global only | Show context window fraction |
contextBar.showProgressBar | boolean | true | Global only | Show context window progress bar |
contextBar.showRateLimits | boolean | true | Global only | Show adapter-reported rate-limit bars (Claude reports 5-hour session and 7-day weekly windows; hidden for adapters that don’t report rate limits) |
The model and effort pills are always visible when the adapter reports them. Both pills double as click-targets for the per-task model/effort picker popover, so they no longer have show/hide toggles. Effort levels are surfaced for adapters whose discoverCapabilities reports a non-empty effortLevels list (Claude Code in particular).
Sidebar settings
Section titled “Sidebar settings”| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
sidebar.width | number | 400 | Global only | Sidebar width in pixels |
Browser settings
Section titled “Browser settings”Settings for the embedded browser pane.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
browser.enabled | boolean | true | Both | When false, the Browser pill is hidden in the task detail window header, and an agent calling kangentic_browser_open_pane is refused with a message telling it to ask you to enable the pane |
browser.defaultUrl | string | undefined | undefined | Both | Project-default URL used when a task has no per-task override. Set only deliberately, via Save as project default in the pane or this setting; a task navigation never seeds it (it did before v0.36.0). Stored per-project; never seeded into new projects |
Agent Browser settings
Section titled “Agent Browser settings”A cross-project security policy governing whether and how an agent may drive the embedded Browser pane via the kangentic_browser_* MCP tools. These are separate from the per-project browser.* pane settings above and live under the Agent Browser settings tab. They are resolved with the defaults below when unset (they are not written into the built-in DEFAULT_CONFIG).
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
browserAutomation.enabled | boolean | true | Global only | Master switch. When false, the kangentic_browser_* tools are not registered at all |
browserAutomation.allowInteraction | boolean | true | Global only | Allow every tool that acts on the page: click, hover, type, keypress, scroll, drag, select option, drop files, handle dialog, and set viewport. When false, agents are observe-only (screenshots, DOM reads, console, and network requests still work) |
browserAutomation.allowNavigation | boolean | true | Global only | Allow every tool that loads or reloads a page: navigate, history, open pane, pop out, and dock. Turn off to confine agents to the page you loaded |
browserAutomation.allowEval | boolean | false | Global only | Allow arbitrary JavaScript eval in the loaded page (kangentic_browser_eval). Off by default - the one unbounded primitive, running with the page’s cookies |
browserAutomation.restrictNavigationToLocalhost | boolean | false | Global only | Only allow navigation to localhost / private hosts, never public sites. Off by default (any http(s) URL allowed) |
Dev-server ports
Section titled “Dev-server ports”The scan range Kangentic draws from when an agent asks for a free dev-server port with kangentic_reserve_dev_ports. Config-file only: there is no Settings UI, and the keys are not written into the built-in DEFAULT_CONFIG.
Kangentic never starts or supervises a dev server, and nothing is reserved up front. A task holds ports only once its agent has asked for them, and the reservation lives until the task or its project is deleted.
The ledger itself is deliberately in the global database, not per project: ports are a shared resource, so a per-project ledger could not see that another project already holds 4200. That scopes it to one Kangentic instance rather than the whole machine. A bind probe, run against every candidate before it is handed out, is what keeps two instances (or any other process) from colliding.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
devServer.portRangeStart | number | 7300 | Global only | First port considered when reserving |
devServer.portRangeEnd | number | 7499 | Global only | Last port considered when reserving, inclusive |
Dictation, Knowledge Graph, and notifications
Section titled “Dictation, Knowledge Graph, and notifications”The global subsystems that have their own Settings tabs: Workflows, Dictation, Knowledge Graph, and Notifications.
Workflow settings
Section titled “Workflow settings”The two agents in Settings > Workflows, new in v0.46.0. Global/shared scope. The Workflow agent (workflows.*) plans each workflow; the Automation agent (automation.*) answers every automation’s Condition, judges whether a workflow task’s column work is done, and names tasks and Command Terminals. Each is its own choice, with no fallback to the project’s default agent or to the Knowledge Graph’s.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
workflows.agent | string | null | null | Global only | The Workflow agent, as an adapter name. Only an agent that reaches the MCP server with an address of its own and can resume a conversation is offered. While it is unset, no workflow’s agent starts |
workflows.model | string | null | null | Global only | The model it plans with, as that CLI’s own id. Required when the agent’s CLI takes one. Cleared whenever workflows.agent changes |
workflows.effort | string | null | null | Global only | Its effort level. Unset runs at the CLI’s own level |
workflows.agentPrompt | string | null | null | Global only | The whole first message a new workflow’s agent gets, with {{projectName}}, {{projectPath}}, {{baseBranch}}, {{columns}}, and {{brief_xml}}. Unset or blank sends the built-in prompt, and a value equal to it is stored as unset, so a later change to the default reaches you. An edit applies to workflows whose agent starts after it |
workflows.agentPaneWidth | number | null | null | Global only | The Workflows view’s agent pane width in pixels, set by dragging its edge. Unset is 560 |
automation.agent | string | null | null | Global only | The Automation agent, as an adapter name, from the agents that can answer a one-shot question. While it is unset, no Condition can be switched on, every workflow column waits for a person to move the task on, and naming is off |
automation.model | string | null | null | Global only | The model it answers with. Required where the agent’s answer run takes a model. Cleared whenever automation.agent changes |
automation.effort | string | null | null | Global only | Its effort level. Unset runs at the agent’s recommended level |
Dictation settings
Section titled “Dictation settings”Local push-to-talk voice-to-text into whatever text field or terminal holds focus. Global/shared scope (hardware and user level, not per-project); lives under the Dictation settings tab. The push-to-talk key binding is set via keybindings (the dictation.pushToTalk action), not here.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
dictation.enabled | boolean | false | Global only | Master toggle: show the mic button and enable push-to-talk |
dictation.engineMode | string | "auto" | Global only | Transcription engine selection: auto (tiers by detected hardware), sherpa-onnx, whisper-cpp, hybrid, or remote (uses the dictation.remote cloud endpoint below). The non-auto values force a specific engine for latency-vs-accuracy comparison |
dictation.modelId | string | null | null | Global only | The refinement (final) model, read only while dictation.mode is custom. null = the default preset’s; "none" = keep the live text with no refinement pass |
dictation.liveModelId | string | null | null | Global only | The live (preview) model, read only while dictation.mode is custom. null = the Light preset’s; "none" = no live preview |
dictation.mode | string | (derived) | Global only | The Mode choice: accurate (shown as Best), balanced (Balanced), fast (Light), or custom. A preset saves no model ids. Kangentic resolves it to models for the chosen language each session, so it follows the lineup when a release changes it. custom runs liveModelId and modelId. Unset reads as the machine’s default preset, Best on a capable machine and Light on a low-end one |
dictation.language | string | "en" | Global only | BCP-47 spoken language. A preset picks models that cover it, and the Custom dropdowns narrow to them |
dictation.autoSubmit | boolean | true | Global only | Press Enter automatically after inserting the text; set false to leave it in the input for review. Refused regardless of this setting for a field inside a multi-field form, and for any field inside a page loaded in the Browser pane - see Dictation |
dictation.releaseBufferMs | number | 250 | Global only | Keep the mic open this many ms after releasing push-to-talk so the last word isn’t clipped (0-500, snaps to 50ms steps; 0 = stop immediately) |
dictation.experience | string | "popup" | Global only | Reserved; nothing reads it. The live transcript always types into the resolved target as you speak, each revision replacing the last in place; the popup and docked surfaces the key names are retired |
dictation.remote | object | (unset) | Global only | OpenAI-compatible /v1/audio/transcriptions endpoint for the final text, used when engineMode is remote. The live preview still runs on-device; only the final clip is sent |
dictation.punctuation is gone in v0.45.0. It reached only a test engine, and a stored value is ignored.
Knowledge Graph settings
Section titled “Knowledge Graph settings”The conversation index and the Knowledge Graph. Global/shared scope (per-machine); lives under the Knowledge Graph settings tab, next to Dictation. Kangentic indexes each agent conversation, each task’s own record, and the default branch’s commits into a local per-project index that powers the Quick Find Conversations group (by keyword), the Knowledge Graph, and the kangentic_search MCP tool (for agents). Keyword search is on by default; the Knowledge Graph is opt-in. See Conversation memory for the index.
These keys lived under memory through v0.43.2. The first launch of v0.44.0 reads that block once and writes it as knowledgeGraph: indexingEnabled and acceleration keep their names, semanticEnabled becomes enabled, embeddingModel becomes localModel, and a key already set under its new name wins.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
knowledgeGraph.indexingEnabled | boolean | true | Global only | The Index switch: index agent conversation transcripts, tasks, and commits locally for search and recall. When false, no indexing runs, no conversation hits appear in Quick Find or kangentic_search, and the embedding worker never starts. Fully local and keyless |
knowledgeGraph.enabled | boolean | false | Global only | The Knowledge Graph switch: the semantic (embedding) layer on top of keyword search, which finds work by meaning and draws the map, plus the agent rows below it. Turning it on triggers a local download of the selected knowledgeGraph.localModel and background embedding of the index, and turns indexing on too. Keyword search works regardless; if the model or vector extension is unavailable, a semantic query transparently falls back to keyword-only |
knowledgeGraph.localModel | string | "granite-r2" | Global only | Which local embedding model finds by meaning, chosen with Search quality. Options (ONNX, keyless, offline): granite-r2 (Best, IBM’s Granite embedding English R2, 768d, ~153 MB) and bge-small (Light, bge-small-en-v1.5, 384d, ~33 MB). A config that still names bge-base or bge-large, the models v0.45.0 removed, reads as granite-r2. Switching re-embeds the index in the background, with search answering by keyword alone until it is done |
knowledgeGraph.acceleration | string | "auto" | Global only | Which hardware the embedding model runs on, labeled Model acceleration: auto (default) and gpu prefer a GPU execution provider (DirectML on Windows, WebGPU elsewhere) and fall back to CPU; cpu forces the universal path. It has nothing to do with graphicsAccelerationEnabled, which is how the app itself draws |
knowledgeGraph.agent | string | null | null | Global only | Which agent answers Knowledge Graph questions and writes task summaries, as an adapter name (claude, codex). No fallback to a project’s agent: while it is unset, Ask, task summaries, and the source code index wait |
knowledgeGraph.model | string | null | null | Global only | The model that agent uses for answers and summaries, as that CLI’s own id. Required wherever the agent’s answer run takes a model. Cleared whenever knowledgeGraph.agent changes |
knowledgeGraph.effort | string | null | null | Global only | The effort level answers run at, from the levels the agent’s CLI reports. Unset runs at the agent’s recommended level (low wherever one is declared). Summaries ignore it. Cleared when the agent changes |
knowledgeGraph.taskSummaries | boolean | true | Global only | Let the agent write a sentence or two about each Done task, searched with the task and handed to Ask. Waits, spending nothing, until the Knowledge Graph has an agent and a model. false stops new calls and keeps the summaries already written |
knowledgeGraph.sourceCode | boolean | true | Global only | Index each project’s default branch (source files and docs, not tests, fixtures, data, lock files, build output, or files over 256 KB) so answers can use the code. Waits for the Knowledge Graph and its agent. false clears the open project’s code index at once and another project’s on its next open |
Notification settings
Section titled “Notification settings”| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
notifications.desktop.onAgentIdle | boolean | true | Global only | Desktop notification when an agent finishes its turn or needs permission. Suppressed only while the window is focused and that session’s project is the open one, so an unfocused window still alerts on the open project |
notifications.desktop.onAgentCrash | boolean | true | Global only | Desktop notification when a session exits with an error |
notifications.desktop.onPlanComplete | boolean | true | Global only | Desktop notification when planning completes |
notifications.desktop.onWorkflowComplete | boolean | true | Global only | Desktop notification when every task in a workflow reaches Done |
notifications.desktop.onSpawnStalled | boolean | true | Global only | Desktop notification when a task spawn has been preparing (worktree/git) longer than the stall threshold |
notifications.toasts.onAgentIdle | boolean | true | Global only | In-app toast when an agent on the open project finishes its turn or needs permission. Skipped while that session’s terminal is already on screen: a task detail window, the in-app or detached Agent Monitor, or a phone streaming it |
notifications.toasts.onAgentCrash | boolean | true | Global only | In-app toast when a session exits, cleanly or with an error. The notifications.desktop.onAgentCrash row above fires on error exits only |
notifications.toasts.onPlanComplete | boolean | true | Global only | In-app toast when planning completes |
notifications.toasts.onWorkflowComplete | boolean | true | Global only | In-app toast when every task in a workflow reaches Done, on the workflow’s own project |
notifications.toasts.onSpawnStalled | boolean | true | Global only | In-app toast (with a Cancel action) when a task spawn stalls preparing its worktree |
notifications.toasts.durationSeconds | number | 4 | Global only | How long toasts display |
notifications.toasts.maxCount | number | 5 | Global only | Maximum simultaneous toasts on screen |
notifications.cooldownSeconds | number | 10 | Global only | Minimum seconds between repeat desktop notifications of the same kind for one session. Idle and crash count separately, so a crash is never suppressed by a recent idle alert |
Board columns
Section titled “Board columns”What a column stores, in the project database and in kangentic.json.
Column-level settings
Section titled “Column-level settings”Each column (swimlane) can override settings individually:
| Field | Type | Default | Description |
|---|---|---|---|
description | string | null | null | Free-form description of the column’s purpose. Shown as a header tooltip and shared with your team via kangentic.json. null when unset |
permission_mode | string | null | null | Permission mode override for this column |
auto_spawn | boolean | true | Whether moving a task here spawns an agent |
auto_command | string | null | null | Retired in v0.42.0. The column’s message to its agent is a send_message automation now; the upgrade moved every existing value into one and cleared this field, and nothing reads it |
auto_command_mode | string | 'immediate' | Retired with auto_command. The delivery choice is the send_message automation’s own mode field |
plan_exit_target_id | string | null | null | Target column when plan-mode agent exits |
agent_override | string | null | null | Use a specific agent for sessions in this column (one of "claude", "codex", "gemini", "cursor", "copilot", "grok", "antigravity", "goose", "opencode", "aider", "qwen", "kimi", "droid", "warp", "ollama"). null inherits the project default agent |
model_override | string | null | null | Adapter-specific model identifier passed at spawn (e.g. Claude --model opus, Codex --model gpt-5-codex). On a column transition a running session is suspended and resumed with the new flag, conversation intact; Kangentic never types /model |
effort_override | string | null | null | Adapter-specific effort/reasoning level passed at spawn (e.g. Claude --effort xhigh). On a column transition a running session at another level is suspended and resumed with the new flag, as for a model; Kangentic never types /effort |
handoff_context | boolean | false | When enabled, point the next agent at the prior agent’s native session history file when a task moves into this column. Any agent can receive one; whether context carries depends on the outgoing agent. Claude, Codex, Gemini, Grok, Antigravity, OpenCode, Aider, Qwen, Kimi, and Droid leave a readable history; Cursor, Copilot, Goose, Warp, and Ollama leave nothing Kangentic can hand on |
session_target | string | 'main' | Which session track a task runs on in this column: main (the task’s shared main conversation, resumed as it moves between normal columns) or isolated (this column’s own separate, context-isolated session, keyed by the column). Leaving an isolated column resumes the main session. Forced to main for To Do / Done columns |
session_spawn_strategy | string | 'create_or_resume' | What to do with that session track on column entry: create_or_resume (resume the track’s session if one exists, else spawn) or always_spawn_new (always spawn a fresh, independent pass, retiring the prior session). The Column Manager and the MCP column tools set it for you when session_target changes: isolated carries it to always_spawn_new, and main back to create_or_resume. A hand edit of kangentic.json does not, so name both keys there. Governs column entry only - an app restart or pause/resume still resumes an in-progress session |
The available models and effort levels are surfaced from each adapter’s discoverCapabilities probe - the Column Manager’s dropdowns show only what the active CLI supports. See Adding and editing columns for details on how these interact.
Column automations
Section titled “Column automations”What a column does when a task enters it, each time its agent finishes a turn there, and when the task leaves. Each column owns one ordered list, split into On task enter, On agent turn end, and On task exit (onEnter, onTurnEnd, and onExit in kangentic.json), and each row has its own switch. They are edited in the Column Manager (click a column header on the board) and round-trip through kangentic.json under the column that owns them.
Five types ship:
| Type | What it does | Fields |
|---|---|---|
send_message | Types a message at the column’s agent. At a turn end the turn is over, so it sends at once whatever mode says | message, mode (immediate or deferred) |
run_script | Runs a script in the task’s worktree, or the project checkout when it has none | script, timeoutMinutes (1 to 120, default 10, file only) |
webhook | Calls a URL, making up to 3 attempts on a transport error, 408, 429, or 5xx | url, method, body, headers |
notify | Raises one desktop notification | title, body |
move_to_column | Moves the task to another column, with an optional message for the agent there. On agent turn end only, and a row that runs ends its group | targetColumn (a column name; absent or empty means the next column, which for a workflow task is the next on its route), message |
Every row can also carry condition, finalLine, and maxRuns beside its type’s fields. condition is an Agent evaluation, plain words the Automation agent (automation.agent) answers yes or no before the row runs. finalLine is a Text match, a pattern checked in code against the last line of the agent’s final message, with no agent call; it works on onTurnEnd rows only, and a row carries one kind or the other. maxRuns is the row’s Frequency, the most times it runs on one task, counted across visits and started over when the task leaves Done. See Automations for how each behaves.
A sixth, spawn_agent, is legacy: it survives on boards that already had one carrying a custom prompt, and cannot be created. A name is required and unique within its column, ignoring case. Every text field takes the template variables, and a script also receives each one as a KANGENTIC_* environment variable. The rows are stored in the per-project database (column_automations), with every execution recorded in automation_runs; the file shape is in Board configuration and the behavior in Automations.
Themes
Section titled “Themes”Kangentic includes 12 built-in themes, picked from a swatch grid in Settings > Theme that shows each one in its own colors:
- Graphite (
dark) - The neutral dark default - Rust - The product theme, built from kangentic.com’s own colors
- Moon - Cool-toned dark theme
- Forest - Green-accented dark theme
- Ocean - Blue-accented dark theme
- Ember - Warm orange-accented dark theme
- Paper (
light) - The neutral light default - Clay - The product theme’s light half
- Sand - Warm neutral light theme
- Mint - Green-accented light theme
- Sky - Blue-accented light theme
- Peach - Warm pink-accented light theme
Set the theme in the settings panel or in your global config file. Graphite and Paper keep their original ids, dark and light; the others are their lower-cased names. Turn on Follow system appearance to keep one theme for a light OS and another for a dark one (themeFollowsSystem, themeLight, themeDark); the app switches the moment the OS does. See Settings.
Global-only settings
Section titled “Global-only settings”These settings cannot be overridden per-project:
agent.maxConcurrentSessionsagent.queueOverflowagent.cliPathsagent.idleTimeoutMinutesagent.autoResumeSessionsOnRestartagent.launchOptionsagent.executionServerssidebarVisibleskipDeleteConfirmskipBoardConfigConfirmautoFocusIdleSessionstopLeftoverProcessesautoNameRateLimitPerHourboardLayoutcardDensitycardPreviewcolumnWidthshowTaskNumbersterminalPanelVisiblestatusBarVisibleanimationsEnabledgraphicsAccelerationEnabledgraphicsAccelerationOffBydiffViewModediffDefaultScopediffIgnoreWhitespacediffCollapseUnchangeddiffWrapLinesdiffUseInlineWhenNarrowdiffFileSortdiffFlatListwindowLightDismisssidebar.widthterminal.shellterminal.fontFamilyterminal.fontSizeterminal.cursorStyleterminal.colorsterminal.backspaceSendsCtrlHterminal.panelHeightterminal.panelCollapsedterminal.showPreviewrestoreWindowPositionstatusBarPeriod(deprecated)usageStatsPeriodusageStatsScopemonitor.*(all Agent Monitor view settings)mcpServer.enabledmcpServer.bindAddressmcpServer.callbackHostnotifications.*(all notification settings)contextBar.*(all context bar settings)browserAutomation.*(all Agent Browser settings)devServer.*(the dev-server port range)dictation.*(all dictation settings)knowledgeGraph.*(the conversation index and the Knowledge Graph)workflows.*(the Workflow agent and its prompt)automation.*(the Automation agent)mobileBridge.*(all mobile-bridge settings)developer.*(all developer settings)hotkeyOverrides
Network and mobile
Section titled “Network and mobile”The two listeners the app can open: the MCP server for the agents it runs, and the mobile bridge for a paired phone.
MCP server settings
Section titled “MCP server settings”| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
mcpServer.enabled | boolean | true | Global only | Enable/disable the MCP server that lets agents interact with the board |
mcpServer.bindAddress | string | "127.0.0.1" | Global only | Network interface the in-process MCP HTTP server listens on. Not exposed in the Settings UI; edit config.json directly. The loopback default keeps the server local; to reach it from another machine use a wildcard like 0.0.0.0 (which still binds loopback), never a single non-loopback interface. Read once at startup |
mcpServer.callbackHost | string | undefined | unset | Global only | Extra hostname allowlisted alongside bindAddress for the server’s DNS-rebinding protection, so a legitimate external request is not rejected. Not exposed in the Settings UI; edit config.json directly |
When enabled, each agent session gets access to MCP tools for creating tasks, searching the board, browsing the backlog, and reading session transcripts. See the MCP tool reference for the full list of tools. As of v0.15.0 the MCP server runs as an in-process streamable HTTP transport (replacing the earlier file-bridge). Disable this if you don’t want agents to be able to modify the board.
As a runaway-loop safeguard, the MCP server caps task creation at 500 tasks per app launch. Once the cap is hit, kangentic_create_task returns an error until you restart Kangentic. This is a fixed limit, not a configurable setting.
Mobile bridge settings
Section titled “Mobile bridge settings”The desktop half of the mobile companion app link, configured in Settings > Mobile Devices. These keys are global: they describe this desktop installation, not any one project. The bridge ships in production builds and is off until you turn it on.
| Key | Type | Default | Scope | Description |
|---|---|---|---|---|
mobileBridge.enabled | boolean | false | Global only | The Mobile bridge switch. While off, no relay connection is held, pairing is unavailable, and the tab hides its Relay and Phones cards |
mobileBridge.relayMode | string | resolves to "hosted" | Global only | Which relay to dial: hosted (the Kangentic-operated relay at wss://relay.kangentic.com), or custom (your own, from relayUrl). A third value, local, is dev-build only: a shipped build reports and dials hosted even if a persisted local reaches it. Deliberately absent from the shipped defaults rather than set to "hosted", so that a config written before this key existed, but carrying a relayUrl, is still inferred as custom instead of being silently moved onto the hosted relay |
mobileBridge.relayUrl | string | "" | Global only | The relay address to dial, consulted only when relayMode is custom. Must be wss://, or ws:// for loopback only. An empty or invalid value falls back to the hosted relay, so the dialed address is never blank |
Pairing, the paired-device roster, and the install link are UI surfaces on the same tab rather than config keys. See Pairing a phone for the ceremony and Kangentic Relay for what the relay does and how to run your own.
Developer and diagnostics
Section titled “Developer and diagnostics”Flags for people working on Kangentic itself, the environment variables the app reads, and what leaves the machine.
Developer settings
Section titled “Developer settings”Power-user toggles for diagnosing the activity engine and other internal subsystems. All are global-only. In shipped builds they default to off; several turn on automatically in dev builds (npm start and /preview), as noted per key below. Toggle the whole panel from anywhere with Ctrl+Shift+D / Cmd+Shift+D.
| Key | Type | Default | Description |
|---|---|---|---|
developer.activityDebugOverlay | boolean | false | Show the floating activity-engine debug overlay (live counters, current activity reason, and recent transitions per running session). Also writes a per-session JSON snapshot under .kangentic/debug/ on each state change |
developer.persistConsoleLogs | boolean | dev: true, prod: false | Persist info/debug/log console output to .kangentic/logs/<date>.log, or to <configDir>/logs/<date>.log while no project is open (errors and warnings are always persisted). Read via the kangentic_tail_logs MCP tool, which merges both locations |
developer.recordIpcTraffic | boolean | false | Record every IPC handler call (channel, args, result, duration) to .kangentic/logs/ipc-<date>.jsonl. Sensitive channels are redacted. Off in production and regular dev; auto-enabled only in the ephemeral /preview instance, whose logs are wiped on close. Non-trivial disk impact |
developer.previewInspectionServer | boolean | dev: true, prod: false | Bind a localhost-only HTTP inspection bridge powering the dev-only devtools MCP tools. The UI affordance is excluded from production builds; the key persists for type compatibility but has no effect in shipped binaries |
developer.previewEvalEnabled | boolean | dev: true, prod: false | Stricter gate on top of previewInspectionServer that enables high-risk inspection endpoints (eval, synthetic session events, raw PTY input). On in dev builds, like previewInspectionServer; the UI affordance is dropped from production builds |
Environment variables
Section titled “Environment variables”| Variable | Description |
|---|---|
KANGENTIC_DATA_DIR | Override the config/data directory path. Takes priority over the launcher’s --data-dir flag when both are set |
KANGENTIC_TELEMETRY | Set to 0 to disable all anonymous telemetry: usage analytics and error reporting |
KANGENTIC_ERROR_REPORTING | Set to 0 to disable crash and error reporting only, leaving usage analytics on |
KANGENTIC_ANNOUNCEMENTS_URL | Point the in-app announcements feed at a different URL (see Notifications) |
Telemetry and analytics
Section titled “Telemetry and analytics”Kangentic sends anonymous usage analytics to Aptabase and crash and error reports to Sentry. No code or task content is collected, one per-launch event carries a stable install identifier, and stack-frame paths are normalized to the app root before an error report leaves your machine - see Privacy and analytics. Set KANGENTIC_TELEMETRY=0 to turn off both, or KANGENTIC_ERROR_REPORTING=0 to turn off error reporting alone.
See Privacy and analytics for full details on what is collected and how to opt out.
See also
Section titled “See also”- Settings - the same keys as the panel presents them
- Board configuration - the per-project
kangentic.jsonandkangentic.local.jsonfiles - Columns - what the column-level keys do to a session
- Automations - what the
automationsrows do - Privacy and analytics - what the telemetry keys and environment variables switch
Kangentic is free and open source. A star helps other people find it.
Star on GitHub