Skip to content

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.

Kangentic resolves settings in three layers:

  1. Built-in defaults - sensible defaults for all settings
  2. Global user config - your personal defaults for all projects
  3. Project overrides - per-repository settings

Effective config = deep-merge(defaults, global config, project config).

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.

Global: %APPDATA%\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.

The keys behind the Board, Task, Changes, and Terminal tabs and the two dashboards.

KeyTypeDefaultScopeDescription
themestring"dark"BothThe hand-picked UI theme, painted while themeFollowsSystem is off: dark (Graphite), light (Paper), rust, clay, moon, forest, ocean, ember, sand, mint, sky, peach
themeFollowsSystembooleanfalseBothPaint 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
themeLightstring"light"BothThe theme for a light OS appearance when following the system. A dark-base value here falls back to light
themeDarkstring"dark"BothThe theme for a dark OS appearance when following the system. A light-base value here falls back to dark
sidebarVisiblebooleantrueGlobal onlyShow/hide the sidebar
boardLayoutstring"horizontal"Global onlyReserved; nothing reads it and the Board tab offers no control for it. The board scrolls sideways
cardDensitystring"default"Global onlyTask card density: compact, default, or comfortable
cardPreviewstring"agent-latest-message"Global onlyWhat 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
columnWidthstring"default"Global onlyColumn width: narrow, default, or wide
showTaskNumbersbooleantrueGlobal onlyShow each task’s #N (display ID) number on its board card. Labeled Ticket numbers in Settings > Task
terminalPanelVisiblebooleantrueGlobal onlyShow the bottom terminal panel
statusBarVisiblebooleantrueGlobal onlyShow the status bar at the bottom of the window
animationsEnabledbooleantrueGlobal onlyEnable 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
graphicsAccelerationEnabledbooleantrueGlobal onlyChromium 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
graphicsAccelerationOffBystring | nullnullGlobal onlyWho 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
diffViewModestring"split"Global onlyDefault layout for Git file diffs in the Changes panel: split (side by side) or inline (unified)
diffDefaultScopestring"working"Global onlyScope a freshly opened Changes panel shows: working (uncommitted edits), staged (index), or branch (the full branch vs its base)
diffIgnoreWhitespacebooleanfalseGlobal onlyHide whitespace-only changes in the diff to filter out reformatting noise
diffCollapseUnchangedbooleanfalseGlobal onlyFold away large unchanged regions, showing only the changed hunks with a little context
diffWrapLinesbooleanfalseGlobal onlySoft-wrap long lines onto the next row instead of scrolling the diff horizontally. Applies to both split and inline mode
diffUseInlineWhenNarrowbooleantrueGlobal onlyRender 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
diffFileSortstring"name"Global onlyChanges 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
diffFlatListbooleanfalseGlobal onlyShow changed files as a flat list of full paths instead of a nested directory tree
windowLightDismissstring"focused"Global onlyWhether 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
skipDeleteConfirmbooleanfalseGlobal onlySkip confirmation dialog on task delete
skipBoardConfigConfirmbooleanfalseGlobal onlySkip confirmation when applying board config changes
autoFocusIdleSessionbooleanfalseGlobal onlyAuto-switch to session tab when agent goes idle
stopLeftoverProcessesbooleantrueGlobal onlyStop 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
autoNameRateLimitPerHournumber60Global onlyMaximum 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)
restoreWindowPositionbooleantrueGlobal onlyRestore window position and size from last session
hotkeyOverridesRecord<string, string>{}Global onlyRebound 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
statusBarPeriodstring"live"Global onlyDeprecated: 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

Persistence for the Usage Stats. Both values are one global selection shared across all projects.

KeyTypeDefaultScopeDescription
usageStatsPeriodstring"live"Global onlySaved time range for the usage dashboard: live, today, week, month, all
usageStatsScopestring"project"Global onlySaved dashboard scope: project (current project) or all (app-wide rollup)

Persisted view preferences for the Agent Monitor, shared by the in-app overlay and the pop-out window.

KeyTypeDefaultScopeDescription
monitor.layoutstring"cards"Global onlyMonitor layout: cards, table, or list
monitor.groupBystring"project"Global onlyGrouping: project or state
monitor.sortstring"longest-running"Global onlyRow order within a group: longest-running or recently-started
monitor.liveOnlybooleanfalseGlobal onlyShow 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.textFilterstring""Global onlyPersisted toolbar filter text
monitor.projectFilterstring[][]Global onlyProject 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.stateFilterstring[][]Global onlyRestrict 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
KeyTypeDefaultScopeDescription
backlog.prioritiesarraySee belowBothCustomizable priority levels with labels and colors
backlog.labelColorsobject{}BothDefault color mappings for backlog labels ({ "label-name": "#hex" })

Default priorities:

IndexLabelColor
0None#6b7280
1Low#3b82f6
2Medium#eab308
3High#f97316
4Urgent#ef4444
KeyTypeDefaultScopeDescription
terminal.shellstring | nullnullGlobal onlyShell executable path. null = auto-detect
terminal.fontFamilystring"Menlo, Consolas, \"Courier New\", monospace"Global onlyTerminal font family. The Settings picker is a system-font autocomplete populated from your installed fonts
terminal.fontSizenumber14Global onlyTerminal font size (px), from 8 to 32
terminal.cursorStylestring"block"Global onlyCursor style: block, underline, or bar
terminal.backspaceSendsCtrlHbooleanfalseGlobal onlyLabeled 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.colorsTerminalColorOverrides{}Global onlyCustom 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.showPreviewbooleanfalseGlobal onlyReserved; nothing reads it. What a card shows under its title is cardPreview above
terminal.panelHeightnumber250Global onlyBottom panel height (px)
terminal.panelCollapsedbooleanfalseGlobal onlyPersisted collapsed state of the bottom terminal panel

Which CLI a column starts, how it is allowed to act, and how worktrees, branches, and pull requests behave.

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.

KeyTypeDefaultScopeDescription
agent.permissionModestring"acceptEdits"BothDefault permission mode for spawned agents
agent.cliPathsobject{}Global onlyPer-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.launchOptionsobject{}Global onlyPer-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.executionServersobject{}Global onlyRemote-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.executionobject{}BothPer-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.maxConcurrentSessionsnumber8Global onlyMax concurrent PTY sessions across all agents
agent.queueOverflowstring"queue"Global onlyStored 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.idleTimeoutMinutesnumber0Global onlyAuto-suspend sessions after N minutes idle. 0 = disabled
agent.autoResumeSessionsOnRestartbooleantrueGlobal onlyWhen 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.

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:

ModeBehaviorSupported by
planRead-only tools auto-approved; edits require approvalClaude, Codex, Gemini, Copilot, Grok, Antigravity, Goose (chat only, no file access), OpenCode (Plan agent), Aider, Qwen, Kimi, Warp
defaultUses 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)
acceptEditsFile edits auto-approved; other actions prompt (OpenCode: maps to its Build agent)Claude, Codex, Gemini, Copilot, Grok, Antigravity, OpenCode, Aider, Qwen
bypassPermissionsFull autonomy, no permission prompts at allClaude, Codex, Gemini, Cursor, Copilot, Grok, Antigravity, Goose, Aider, Qwen, Kimi, Warp
dontAskDeny everything not already allowed, no interactive promptsClaude, Codex, Copilot, Grok
autoClassifier-driven auto-approvalClaude, Copilot, Grok

Per-agent defaults:

AgentDefault permission modeNotes
Claude CodeacceptEdits
Codex CLIacceptEditsThe 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 CLIacceptEdits
Cursor CLIbypassPermissionsNon-interactive mode is the default so the stream-JSON init event fires and the ContextBar model pill resolves
GitHub Copilot CLIacceptEdits
Grok BuildacceptEditsAll six Kangentic modes pass through 1:1 via --permission-mode; labels describe grok’s own behavior
Antigravity CLIacceptEditsMapped onto agy’s autonomy flags (--mode plan, --mode accept-edits, --dangerously-skip-permissions); default is agy’s request-review mode
Goose CLIdefaultModes 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
OpenCodeacceptEditsLabels surface OpenCode’s native Plan and Build agents - selecting Plan maps to OpenCode’s read-only built-in agent
AiderbypassPermissionsAider is an edit-first CLI that expects broad file-write access
Qwen CodeacceptEditsLabeled “Auto Edit” - soft fork of Gemini CLI
Kimi CodedefaultLabels: Plan, Default (Confirm Actions), YOLO
Factory DroiddefaultSingle “Default” entry; autonomy is controlled in the Droid TUI (Shift+Tab cycles low/medium/high)
Warp (Oz CLI)defaultPermission-mode labels are informational only; Warp manages permissions via agent profiles (--profile)
OllamadefaultA single informational Chat entry; no flag is injected

Permission mode is resolved in priority order:

  1. A column whose permission_mode is plan. A read-only planning phase is a safety guarantee, so it beats every other rung, including a task’s own pin
  2. 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 the plan column above
  3. Column’s permission_mode (if set)
  4. agent.permissionMode from config
KeyTypeDefaultScopeDescription
git.worktreesEnabledbooleantrueBothGive 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.autoCleanupbooleantrueBothDelete branches when worktrees are removed
git.defaultBaseBranchstring"main"BothDefault 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.copyFilesstring[][]BothFiles to copy from repo root into new worktrees
git.initScriptstring | nullnullBothShell 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.linkNodeModulesbooleantrueBothSymlink 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.prAutoRefreshbooleantrueBothKeep 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.autoFetchbooleantrueBothRun 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.prBypassCountsAsReadybooleantrueBothOn 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

The context bar, the project sidebar, the embedded browser, the Agent Browser tools, and the dev-server port range.

KeyTypeDefaultScopeDescription
contextBar.showShellbooleantrueGlobal onlyShow shell name in context bar
contextBar.showVersionbooleantrueGlobal onlyShow agent CLI version
contextBar.showElapsedbooleantrueGlobal onlyShow a ticking wall-clock elapsed time since the session started
contextBar.showCostbooleantrueGlobal onlyShow session cost
contextBar.showToolCallsbooleantrueGlobal onlyShow the cumulative count of completed tool calls. Click the pill for a per-tool breakdown
contextBar.showAgentActivebooleanfalseGlobal onlyShow 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.showTokensbooleantrueGlobal onlyShow token usage
contextBar.showContextFractionbooleantrueGlobal onlyShow context window fraction
contextBar.showProgressBarbooleantrueGlobal onlyShow context window progress bar
contextBar.showRateLimitsbooleantrueGlobal onlyShow 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).

KeyTypeDefaultScopeDescription
sidebar.widthnumber400Global onlySidebar width in pixels

Settings for the embedded browser pane.

KeyTypeDefaultScopeDescription
browser.enabledbooleantrueBothWhen 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.defaultUrlstring | undefinedundefinedBothProject-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

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).

KeyTypeDefaultScopeDescription
browserAutomation.enabledbooleantrueGlobal onlyMaster switch. When false, the kangentic_browser_* tools are not registered at all
browserAutomation.allowInteractionbooleantrueGlobal onlyAllow 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.allowNavigationbooleantrueGlobal onlyAllow 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.allowEvalbooleanfalseGlobal onlyAllow arbitrary JavaScript eval in the loaded page (kangentic_browser_eval). Off by default - the one unbounded primitive, running with the page’s cookies
browserAutomation.restrictNavigationToLocalhostbooleanfalseGlobal onlyOnly allow navigation to localhost / private hosts, never public sites. Off by default (any http(s) URL allowed)

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.

KeyTypeDefaultScopeDescription
devServer.portRangeStartnumber7300Global onlyFirst port considered when reserving
devServer.portRangeEndnumber7499Global onlyLast 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.

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.

KeyTypeDefaultScopeDescription
workflows.agentstring | nullnullGlobal onlyThe 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.modelstring | nullnullGlobal onlyThe model it plans with, as that CLI’s own id. Required when the agent’s CLI takes one. Cleared whenever workflows.agent changes
workflows.effortstring | nullnullGlobal onlyIts effort level. Unset runs at the CLI’s own level
workflows.agentPromptstring | nullnullGlobal onlyThe 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.agentPaneWidthnumber | nullnullGlobal onlyThe Workflows view’s agent pane width in pixels, set by dragging its edge. Unset is 560
automation.agentstring | nullnullGlobal onlyThe 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.modelstring | nullnullGlobal onlyThe model it answers with. Required where the agent’s answer run takes a model. Cleared whenever automation.agent changes
automation.effortstring | nullnullGlobal onlyIts effort level. Unset runs at the agent’s recommended level

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.

KeyTypeDefaultScopeDescription
dictation.enabledbooleanfalseGlobal onlyMaster toggle: show the mic button and enable push-to-talk
dictation.engineModestring"auto"Global onlyTranscription 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.modelIdstring | nullnullGlobal onlyThe 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.liveModelIdstring | nullnullGlobal onlyThe live (preview) model, read only while dictation.mode is custom. null = the Light preset’s; "none" = no live preview
dictation.modestring(derived)Global onlyThe 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.languagestring"en"Global onlyBCP-47 spoken language. A preset picks models that cover it, and the Custom dropdowns narrow to them
dictation.autoSubmitbooleantrueGlobal onlyPress 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.releaseBufferMsnumber250Global onlyKeep 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.experiencestring"popup"Global onlyReserved; 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.remoteobject(unset)Global onlyOpenAI-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.

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.

KeyTypeDefaultScopeDescription
knowledgeGraph.indexingEnabledbooleantrueGlobal onlyThe 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.enabledbooleanfalseGlobal onlyThe 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.localModelstring"granite-r2"Global onlyWhich 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.accelerationstring"auto"Global onlyWhich 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.agentstring | nullnullGlobal onlyWhich 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.modelstring | nullnullGlobal onlyThe 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.effortstring | nullnullGlobal onlyThe 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.taskSummariesbooleantrueGlobal onlyLet 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.sourceCodebooleantrueGlobal onlyIndex 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
KeyTypeDefaultScopeDescription
notifications.desktop.onAgentIdlebooleantrueGlobal onlyDesktop 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.onAgentCrashbooleantrueGlobal onlyDesktop notification when a session exits with an error
notifications.desktop.onPlanCompletebooleantrueGlobal onlyDesktop notification when planning completes
notifications.desktop.onWorkflowCompletebooleantrueGlobal onlyDesktop notification when every task in a workflow reaches Done
notifications.desktop.onSpawnStalledbooleantrueGlobal onlyDesktop notification when a task spawn has been preparing (worktree/git) longer than the stall threshold
notifications.toasts.onAgentIdlebooleantrueGlobal onlyIn-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.onAgentCrashbooleantrueGlobal onlyIn-app toast when a session exits, cleanly or with an error. The notifications.desktop.onAgentCrash row above fires on error exits only
notifications.toasts.onPlanCompletebooleantrueGlobal onlyIn-app toast when planning completes
notifications.toasts.onWorkflowCompletebooleantrueGlobal onlyIn-app toast when every task in a workflow reaches Done, on the workflow’s own project
notifications.toasts.onSpawnStalledbooleantrueGlobal onlyIn-app toast (with a Cancel action) when a task spawn stalls preparing its worktree
notifications.toasts.durationSecondsnumber4Global onlyHow long toasts display
notifications.toasts.maxCountnumber5Global onlyMaximum simultaneous toasts on screen
notifications.cooldownSecondsnumber10Global onlyMinimum 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

What a column stores, in the project database and in kangentic.json.

Each column (swimlane) can override settings individually:

FieldTypeDefaultDescription
descriptionstring | nullnullFree-form description of the column’s purpose. Shown as a header tooltip and shared with your team via kangentic.json. null when unset
permission_modestring | nullnullPermission mode override for this column
auto_spawnbooleantrueWhether moving a task here spawns an agent
auto_commandstring | nullnullRetired 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_modestring'immediate'Retired with auto_command. The delivery choice is the send_message automation’s own mode field
plan_exit_target_idstring | nullnullTarget column when plan-mode agent exits
agent_overridestring | nullnullUse 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_overridestring | nullnullAdapter-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_overridestring | nullnullAdapter-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_contextbooleanfalseWhen 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_targetstring'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_strategystring'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.

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:

TypeWhat it doesFields
send_messageTypes a message at the column’s agent. At a turn end the turn is over, so it sends at once whatever mode saysmessage, mode (immediate or deferred)
run_scriptRuns a script in the task’s worktree, or the project checkout when it has nonescript, timeoutMinutes (1 to 120, default 10, file only)
webhookCalls a URL, making up to 3 attempts on a transport error, 408, 429, or 5xxurl, method, body, headers
notifyRaises one desktop notificationtitle, body
move_to_columnMoves 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 grouptargetColumn (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.

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

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.

These settings cannot be overridden per-project:

  • agent.maxConcurrentSessions
  • agent.queueOverflow
  • agent.cliPaths
  • agent.idleTimeoutMinutes
  • agent.autoResumeSessionsOnRestart
  • agent.launchOptions
  • agent.executionServers
  • sidebarVisible
  • skipDeleteConfirm
  • skipBoardConfigConfirm
  • autoFocusIdleSession
  • stopLeftoverProcesses
  • autoNameRateLimitPerHour
  • boardLayout
  • cardDensity
  • cardPreview
  • columnWidth
  • showTaskNumbers
  • terminalPanelVisible
  • statusBarVisible
  • animationsEnabled
  • graphicsAccelerationEnabled
  • graphicsAccelerationOffBy
  • diffViewMode
  • diffDefaultScope
  • diffIgnoreWhitespace
  • diffCollapseUnchanged
  • diffWrapLines
  • diffUseInlineWhenNarrow
  • diffFileSort
  • diffFlatList
  • windowLightDismiss
  • sidebar.width
  • terminal.shell
  • terminal.fontFamily
  • terminal.fontSize
  • terminal.cursorStyle
  • terminal.colors
  • terminal.backspaceSendsCtrlH
  • terminal.panelHeight
  • terminal.panelCollapsed
  • terminal.showPreview
  • restoreWindowPosition
  • statusBarPeriod (deprecated)
  • usageStatsPeriod
  • usageStatsScope
  • monitor.* (all Agent Monitor view settings)
  • mcpServer.enabled
  • mcpServer.bindAddress
  • mcpServer.callbackHost
  • notifications.* (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

The two listeners the app can open: the MCP server for the agents it runs, and the mobile bridge for a paired phone.

KeyTypeDefaultScopeDescription
mcpServer.enabledbooleantrueGlobal onlyEnable/disable the MCP server that lets agents interact with the board
mcpServer.bindAddressstring"127.0.0.1"Global onlyNetwork 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.callbackHoststring | undefinedunsetGlobal onlyExtra 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.

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.

KeyTypeDefaultScopeDescription
mobileBridge.enabledbooleanfalseGlobal onlyThe Mobile bridge switch. While off, no relay connection is held, pairing is unavailable, and the tab hides its Relay and Phones cards
mobileBridge.relayModestringresolves to "hosted"Global onlyWhich 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.relayUrlstring""Global onlyThe 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.

Flags for people working on Kangentic itself, the environment variables the app reads, and what leaves the machine.

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.

KeyTypeDefaultDescription
developer.activityDebugOverlaybooleanfalseShow 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.persistConsoleLogsbooleandev: true, prod: falsePersist 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.recordIpcTrafficbooleanfalseRecord 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.previewInspectionServerbooleandev: true, prod: falseBind 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.previewEvalEnabledbooleandev: true, prod: falseStricter 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
VariableDescription
KANGENTIC_DATA_DIROverride the config/data directory path. Takes priority over the launcher’s --data-dir flag when both are set
KANGENTIC_TELEMETRYSet to 0 to disable all anonymous telemetry: usage analytics and error reporting
KANGENTIC_ERROR_REPORTINGSet to 0 to disable crash and error reporting only, leaving usage analytics on
KANGENTIC_ANNOUNCEMENTS_URLPoint the in-app announcements feed at a different URL (see Notifications)

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.

Kangentic is free and open source. A star helps other people find it.

Star on GitHub