commands/config/ — Configuration Management Commands

Interactive settings panel for all Claude Code preferences

The /config command opens a full-screen React (Ink) panel with 38+ settings spanning models, themes, permissions, output styles, notifications, and more. Settings are searchable, navigable by keyboard, and changes revert on Escape.

2
Files
38
Settings
8
Submenus
~2.4 k
Lines

📚 What is this & why does it exist?

The config module is the single entry point for viewing and editing every user-facing preference in Claude Code. Instead of requiring manual JSON editing, it provides an interactive terminal UI with search, arrow-key navigation, toggle switches, and sub-panels for complex settings like model selection and theme picking. All changes are persisted across three storage layers (global config, per-source settings, and session AppState) with full revert-on-cancel support.

💡 Real-world analogy

Think of the /config panel like the Settings app on your smartphone. It has categories (Display, Sound, Privacy) just like the config panel has groups (Behavior, Models, Output). Each setting is either a toggle switch or a picker that opens a sub-screen—exactly like tapping "Ringtone" opens a list. The search bar at the top lets you find any setting instantly, and pressing the back button discards unsaved changes—just like hitting Escape in the config panel triggers revertChanges().

Contents

Architecture

flowchart TD
  CMD["/config command"] -->|"load()"| ENTRY["config.tsx"]
  ENTRY -->|"call()"| SETTINGS["<Settings />"]
  SETTINGS --> CONFIG["<Config />"]
  CONFIG --> SEARCH["<SearchBox />"]
  CONFIG --> LIST["Settings List"]
  CONFIG --> SUB{"Submenu?"}
  SUB -->|Theme| TP["<ThemePicker />"]
  SUB -->|Model| MP["<ModelPicker />"]
  SUB -->|OutputStyle| OP["<OutputStylePicker />"]
  SUB -->|Language| LP["<LanguagePicker />"]
  SUB -->|ExternalIncludes| EI["<ClaudeMdExternalIncludesDialog />"]
  SUB -->|AutoUpdates| AU["<Dialog />"]
  CONFIG -->|"save"| GC["getGlobalConfig() / saveGlobalConfig()"]
  CONFIG -->|"save"| SS["updateSettingsForSource()"]
  CONFIG -->|"save"| AS["setAppState()"]

State & Persistence Flow

flowchart LR
  USER["User toggles setting"] --> HANDLER["onChange handler"]
  HANDLER --> GC["saveGlobalConfig()"]
  HANDLER --> SRC["updateSettingsForSource()"]
  HANDLER --> APP["setAppState()"]
  HANDLER --> CHG["setChanges()"]
  GC --> FILE1["~/.claude/config.json"]
  SRC --> FILE2["localSettings / userSettings"]
  APP --> MEM["In-Memory AppState"]
  CHG --> DIRTY["isDirty ref = true"]
  ESC["User presses Escape"] --> REVERT["revertChanges()"]
  REVERT --> SNAP1["Restore initialConfig"]
  REVERT --> SNAP2["Restore initialOutputStyle"]
  REVERT --> SNAP3["Restore initialAppState"]

Key Classes & Functions

Class / FunctionFilePurpose
call()config.tsxEntry point: LocalJSXCommandCall that renders the <Settings> component
Settingsconfig.tsxTop-level React component wrapping <Config> with tab support and close handler
Configconfig.tsxCore component: builds 38 settings items, handles navigation, search, and submenus
ThemePickercomponents/ThemePicker.tsxTerminal theme selection submenu with preview
ModelPickercomponents/ModelPicker.tsxAI model selection submenu with billing info
OutputStylePickercomponents/OutputStylePicker.tsxOutput rendering style picker (concise, verbose, etc.)
LanguagePickercomponents/LanguagePicker.tsxUI language selection submenu
ClaudeMdExternalIncludesDialogcomponents/Manage external CLAUDE.md file includes
ChannelDowngradeDialogconfig.tsxConfirmation dialog for downgrading auto-update channel
onChangeMainModelConfig()config.tsxModel change handler with analytics event and billing check
onChangeVerbose()config.tsxVerbose toggle syncing GlobalConfig + AppState simultaneously
revertChanges()config.tsxRestores all initial snapshots on Escape (full undo)
handleKeyDown()config.tsxKeyboard navigation: arrows, Enter, /, Escape, Tab
useSearchInput()hooks/Hook for search box with query state and cursor offset

Data Structures

TypeNameDescription
typeSettingDiscriminated union: boolean toggle, enum picker, or managedEnum submenu setting
typeSettingBaseBase with id, label (string or ReactNode), optional searchText
unionSubMenu'Theme' | 'Model' | 'TeammateModel' | 'ExternalIncludes' | 'OutputStyle' | 'ChannelDowngrade' | 'Language' | 'EnableAutoUpdates'
typePropsConfig component props: onClose, context, setTabsHidden, contentHeight
typeGlobalConfigPersistent config object from ~/.claude/config.json
typeOutputStyleNamed output rendering style (concise, verbose, etc.)
typePermissionModePermission modes: default, plan, acceptEdits, bypassPermissions, dontAsk, auto
refisDirtyReact ref tracking whether any setting has been modified since panel open
statechangesDictionary Record<string, unknown> tracking all modified settings for change summary

Interactions

DirectionPartnerInterfaceDescription
→utils/configgetGlobalConfig() / saveGlobalConfig()Read/write persistent global config from ~/.claude/config.json
→utils/settingsgetSettingsForSource() / updateSettingsForSource()Read/write per-source settings (localSettings, userSettings)
→state/AppStateuseAppState() / useSetAppState()Read and update session-scoped application state
→utils/modelmodelDisplayString() / isBilledAsExtraUsage()Format model names and check billing tier
→utils/permissionsPERMISSION_MODES / permissionModeTitle()Permission mode options and display names
→utils/fastModegetFastModeModel() / isFastModeSupportedByModel()Fast mode model detection and compatibility check
→utils/analyticslogEvent()Track setting change events for telemetry
→hooks/useKeybindinguseKeybinding()Register keyboard shortcuts (Escape, arrows, Enter)
→hooks/useSearchInputuseSearchInput()Search filtering of settings list
←commands.ts registrydefault exportCommand metadata with aliases: ['settings']

Configuration

Settings Categories (38 items)

CategorySetting IDTypeDescription
BehaviorautoCompactEnabledbooleanAuto-compact long conversations
BehaviorspinnerTipsEnabledbooleanShow tips during loading spinner
BehaviorprefersReducedMotionbooleanReduce UI motion/animation
AIthinkingEnabledbooleanEnable extended thinking mode (shows warning on first toggle)
AIfastModemanagedEnumFast mode auto-switches to CLAUDE_3.5_SONNET
AIspeculationEnabledbooleanSpeculative execution for faster responses
AIdefaultPermissionModeenumValues: default, plan, acceptEdits, bypassPermissions, dontAsk, auto
ModelsmodelmanagedEnumPrimary AI model (opens ModelPicker submenu)
ModelsteammateDefaultModelmanagedEnumTeammate model for agent swarms (visible if swarms enabled)
OutputverbosebooleanVerbose output (syncs GlobalConfig + AppState)
OutputoutputStylemanagedEnumOutput rendering style (opens OutputStylePicker)
OutputthememanagedEnumTerminal theme (opens ThemePicker)
OutputlanguagemanagedEnumUI language (opens LanguagePicker)
FilesfileCheckpointingEnabledbooleanEnable file rewind checkpoints
UpdatesautoUpdatesChannelmanagedEnumAuto-update channel (may open ChannelDowngradeDialog)

Directory Map

Source tree for config
config/
├── index.ts              ← Command registration (type: 'local-jsx', aliases: ['settings'])
└── config.tsx            ← Full implementation: Settings, Config components (~2356 lines)

Deep-Dive Highlights

Core mechanisms from this sub-module have been extracted into dedicated focus areas for in-depth exploration.

Three-Layer Persistence Focus

GlobalConfig (config.json), per-source settings (localSettings/userSettings), and in-memory AppState. Snapshot-based revert on cancel.

saveGlobalConfig + updateSettingsForSource + setAppState

Submenu System Focus

Eight submenu types (Theme, Model, OutputStyle, Language, etc.) with animated transitions and independent keyboard handling.

SubMenu union type dispatching

Search & Navigation Focus

Real-time fuzzy filtering of 38 settings by id, label, or searchText. Arrow keys, Enter, /, Escape, Tab, j/k navigation.

useSearchInput + handleKeyDown

Analytics & Feature Flags Focus

8+ telemetry events tracked on setting changes. Feature flags (TRANSCRIPT_CLASSIFIER, KAIROS, FAST_MODE, CCR_AUTO_CONNECT) conditionally show/hide settings.

logEvent + feature() checks

Code Examples

Example 1: Command Entry Point & Registration

// index.ts — Command registration
import type { Command } from '../../commands.js'

const config = {
  aliases: ['settings'],
  type: 'local-jsx',
  name: 'config',
  description: 'Open config panel',
  load: () => import('./config.js'),
} satisfies Command

export default config

// config.tsx — Entry point
export const call: LocalJSXCommandCall = async (onDone, context) => {
  return <Settings onClose={onDone} context={context} defaultTab="Config" />
}

Example 2: Model Change Handler with Analytics

// config.tsx — onChangeMainModelConfig (simplified)
function onChangeMainModelConfig(value: string | null): void {
  const previousModel = mainLoopModel

  // Track model change analytics
  logEvent('tengu_config_model_changed', {
    from_model: previousModel,
    to_model: value,
  })

  // Update session state
  setAppState(prev => ({
    ...prev,
    mainLoopModel: value,
    mainLoopModelForSession: null,
  }))

  // Record change for summary display
  const displayName = modelDisplayString(value)
  const billingNote = isBilledAsExtraUsage(value, currentPlan)
    ? ' · Billed as extra usage'
    : ''
  setChanges(prev => ({ ...prev, model: displayName + billingNote }))
}

Example 3: Revert on Escape (Snapshot Restore)

// config.tsx — revertChanges
function revertChanges(): void {
  if (!isDirty.current) return

  // Restore all snapshots taken at panel open
  saveGlobalConfig(() => initialConfig.current)
  setGlobalConfig(initialConfig.current)
  setCurrentOutputStyle(initialOutputStyle.current)
  setCurrentLanguage(initialLanguage.current)
  setTheme(initialThemeSetting.current)

  // Restore per-source settings
  updateSettingsForSource('localSettings', initialLocalSettings)
  updateSettingsForSource('userSettings', initialUserSettings)

  // Restore session state
  setAppState(initialAppState)
  setUserMsgOptIn(initialUserMsgOptIn)

  // Clear change tracking
  setChanges({})
}