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.
📚 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 / Function | File | Purpose |
|---|---|---|
call() | config.tsx | Entry point: LocalJSXCommandCall that renders the <Settings> component |
Settings | config.tsx | Top-level React component wrapping <Config> with tab support and close handler |
Config | config.tsx | Core component: builds 38 settings items, handles navigation, search, and submenus |
ThemePicker | components/ThemePicker.tsx | Terminal theme selection submenu with preview |
ModelPicker | components/ModelPicker.tsx | AI model selection submenu with billing info |
OutputStylePicker | components/OutputStylePicker.tsx | Output rendering style picker (concise, verbose, etc.) |
LanguagePicker | components/LanguagePicker.tsx | UI language selection submenu |
ClaudeMdExternalIncludesDialog | components/ | Manage external CLAUDE.md file includes |
ChannelDowngradeDialog | config.tsx | Confirmation dialog for downgrading auto-update channel |
onChangeMainModelConfig() | config.tsx | Model change handler with analytics event and billing check |
onChangeVerbose() | config.tsx | Verbose toggle syncing GlobalConfig + AppState simultaneously |
revertChanges() | config.tsx | Restores all initial snapshots on Escape (full undo) |
handleKeyDown() | config.tsx | Keyboard navigation: arrows, Enter, /, Escape, Tab |
useSearchInput() | hooks/ | Hook for search box with query state and cursor offset |
Data Structures
| Type | Name | Description |
|---|---|---|
| type | Setting | Discriminated union: boolean toggle, enum picker, or managedEnum submenu setting |
| type | SettingBase | Base with id, label (string or ReactNode), optional searchText |
| union | SubMenu | 'Theme' | 'Model' | 'TeammateModel' | 'ExternalIncludes' | 'OutputStyle' | 'ChannelDowngrade' | 'Language' | 'EnableAutoUpdates' |
| type | Props | Config component props: onClose, context, setTabsHidden, contentHeight |
| type | GlobalConfig | Persistent config object from ~/.claude/config.json |
| type | OutputStyle | Named output rendering style (concise, verbose, etc.) |
| type | PermissionMode | Permission modes: default, plan, acceptEdits, bypassPermissions, dontAsk, auto |
| ref | isDirty | React ref tracking whether any setting has been modified since panel open |
| state | changes | Dictionary Record<string, unknown> tracking all modified settings for change summary |
Interactions
| Direction | Partner | Interface | Description |
|---|---|---|---|
| → | utils/config | getGlobalConfig() / saveGlobalConfig() | Read/write persistent global config from ~/.claude/config.json |
| → | utils/settings | getSettingsForSource() / updateSettingsForSource() | Read/write per-source settings (localSettings, userSettings) |
| → | state/AppState | useAppState() / useSetAppState() | Read and update session-scoped application state |
| → | utils/model | modelDisplayString() / isBilledAsExtraUsage() | Format model names and check billing tier |
| → | utils/permissions | PERMISSION_MODES / permissionModeTitle() | Permission mode options and display names |
| → | utils/fastMode | getFastModeModel() / isFastModeSupportedByModel() | Fast mode model detection and compatibility check |
| → | utils/analytics | logEvent() | Track setting change events for telemetry |
| → | hooks/useKeybinding | useKeybinding() | Register keyboard shortcuts (Escape, arrows, Enter) |
| → | hooks/useSearchInput | useSearchInput() | Search filtering of settings list |
| ← | commands.ts registry | default export | Command metadata with aliases: ['settings'] |
Configuration
Settings Categories (38 items)
| Category | Setting ID | Type | Description |
|---|---|---|---|
| Behavior | autoCompactEnabled | boolean | Auto-compact long conversations |
| Behavior | spinnerTipsEnabled | boolean | Show tips during loading spinner |
| Behavior | prefersReducedMotion | boolean | Reduce UI motion/animation |
| AI | thinkingEnabled | boolean | Enable extended thinking mode (shows warning on first toggle) |
| AI | fastMode | managedEnum | Fast mode auto-switches to CLAUDE_3.5_SONNET |
| AI | speculationEnabled | boolean | Speculative execution for faster responses |
| AI | defaultPermissionMode | enum | Values: default, plan, acceptEdits, bypassPermissions, dontAsk, auto |
| Models | model | managedEnum | Primary AI model (opens ModelPicker submenu) |
| Models | teammateDefaultModel | managedEnum | Teammate model for agent swarms (visible if swarms enabled) |
| Output | verbose | boolean | Verbose output (syncs GlobalConfig + AppState) |
| Output | outputStyle | managedEnum | Output rendering style (opens OutputStylePicker) |
| Output | theme | managedEnum | Terminal theme (opens ThemePicker) |
| Output | language | managedEnum | UI language (opens LanguagePicker) |
| Files | fileCheckpointingEnabled | boolean | Enable file rewind checkpoints |
| Updates | autoUpdatesChannel | managedEnum | Auto-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.
Submenu System Focus
Eight submenu types (Theme, Model, OutputStyle, Language, etc.) with animated transitions and independent keyboard handling.
Search & Navigation Focus
Real-time fuzzy filtering of 38 settings by id, label, or searchText. Arrow keys, Enter, /, Escape, Tab, j/k navigation.
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.
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({})
}