How to Add Custom Themes to the kimi-code Terminal UI

You can add custom themes to the kimi-code terminal UI by creating JSON files in ~/.kimi-code/themes/ that define color overrides on top of the built-in dark or light palettes, then activating them via the /theme <name> slash command or the theme setting in tui.toml.

The MoonshotAI/kimi-code terminal interface supports user-defined color schemes that extend the built-in styling system. By placing properly structured theme files in the custom themes directory, you can personalize the TUI with persistent color palettes that merge seamlessly with the base dark or light modes.

Custom Theme Storage Location

Custom themes reside in the user data directory under the themes subdirectory. According to the source code in apps/kimi-code/src/tui/theme/custom-theme-loader.ts, the function getCustomThemesDir() returns join(getDataDir(), 'themes'), which resolves to ~/.kimi-code/themes/ on most systems.

Each theme is a standalone JSON file named <theme-name>.json. The loader exposes two functions for enumeration: listCustomThemes() (async) and listCustomThemesSync() (sync), which read directory entries, strip the .json suffix, and filter out reserved names. These reserved names—dark, light, and auto—cannot be used for custom themes, preventing accidental overrides of built-in palettes.

Creating a Valid Theme File

Theme files must conform to the CustomThemeSchema (a Zod schema) defined in the loader. A valid custom theme JSON structure includes:

  • name: Internal identifier matching the filename (without extension)
  • displayName: Human-readable label shown in the UI
  • base: Either "dark" or "light"—determines which built-in palette serves as the foundation
  • colors: An object mapping token names to hex color strings

The readCustomTheme(name) function validates all color values against the regex ^#[0-9a-fA-F]{6}$. Invalid hex strings are silently ignored, causing those tokens to fall back to the base palette values.

Create a file at ~/.kimi-code/themes/rose.json:

{
  "name": "rose",
  "displayName": "Rosy Dawn",
  "base": "light",
  "colors": {
    "background": "#fff1f0",
    "foreground": "#5a2120",
    "accent": "#d23669",
    "warning": "#e06c75"
  }
}

If you omit the base field, the loader defaults to "dark" when merging colors.

Loading and Merging Custom Themes

The loadCustomThemeMerged(name) function in custom-theme-loader.ts orchestrates theme application. It first calls readCustomTheme(name) to parse and validate the JSON, then retrieves the base palette via getBuiltInPalette(parsed.base) from src/tui/theme/colors.ts. The final color set is created by spreading the base palette and overwriting specific keys with your custom values.

You can load themes programmatically using the loader API:

import { loadCustomThemeMerged } from '#/tui/theme/custom-theme-loader';

async function apply(name: string) {
  const palette = await loadCustomThemeMerged(name);
  if (!palette) {
    console.error(`Theme ${name} not found`);
    return;
  }
  // palette is a complete ColorPalette object ready for the UI renderer
  console.log('Loaded palette:', palette);
}

apply('rose');

To enumerate available custom themes from external scripts:

import { listCustomThemes } from '#/tui/theme/custom-theme-loader';

async function showThemes() {
  const themes = await listCustomThemes();
  console.log('Custom themes:', themes); // e.g., ["rose", "midnight"]
}

showThemes();

Activating Themes in the Terminal UI

Once a theme file exists in ~/.kimi-code/themes/, you can activate it immediately without restarting the application. The TUI driver implements the slash command /theme <name> in src/tui/commands/theme.ts, which invokes loadCustomThemeMerged(name) and updates appState.theme instantly.

Type the following inside the running kimi-code interface:


/theme rose

For persistent configuration across sessions, set the theme in ~/.kimi-code/tui.toml:

theme = "rose"

When the config parser reads tui.toml at startup, it stores the value in appState.theme before rendering begins. If you set theme = "auto", the system uses src/tui/utils/terminal-theme.ts to watch for OSC 11 background-color reports from the terminal and automatically switches between built-in dark and light palettes based on the detected background luminance.

Summary

  • Storage location: Place JSON theme files in ~/.kimi-code/themes/; the loader uses getCustomThemesDir() to locate this path.
  • File format: Valid themes match CustomThemeSchema with a base palette (dark/light) and hex colors validated by ^#[0-9a-fA-F]{6}$.
  • Reserved names: You cannot name themes dark, light, or auto, as these are protected built-in identifiers.
  • Activation: Use the /theme <name> command for immediate switching or set theme = "<name>" in tui.toml for persistence.
  • Merge behavior: loadCustomThemeMerged() spreads your custom colors over the built-in base palette, with invalid colors falling back to defaults.

Frequently Asked Questions

What is the exact file format for kimi-code custom themes?

Custom themes are JSON files that conform to the CustomThemeSchema defined in apps/kimi-code/src/tui/theme/custom-theme-loader.ts. They must include name, displayName, a base palette selector ("dark" or "light"), and a colors object containing valid 6-digit hex codes. The readCustomTheme() function validates these files using Zod and filters out malformed color strings.

Why can't I name my custom theme "dark" or "light"?

The names dark, light, and auto are reserved by the theme system to protect the built-in palettes and the automatic detection mode. The listCustomThemes() and listCustomThemesSync() functions explicitly filter out these reserved strings when enumerating available themes, ensuring users cannot accidentally override critical system themes.

How does kimi-code handle invalid color values in theme files?

When readCustomTheme() parses a theme file, it validates each color string against the regex ^#[0-9a-fA-F]{6}$. Invalid hex codes are silently discarded during the merge process, causing those specific UI tokens to inherit colors from the base palette (dark or light) rather than crashing the loader or displaying errors.

Can I switch themes dynamically without restarting the terminal UI?

Yes. The TUI supports runtime theme switching through the /theme <name> slash command implemented in src/tui/commands/theme.ts. This command calls loadCustomThemeMerged() and updates appState.theme immediately, refreshing the interface without requiring a restart. For automatic switching based on terminal background color, set theme = "auto" in tui.toml to enable OSC 11 detection via src/tui/utils/terminal-theme.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →