# Project-Scoped vs Global Configuration in oh-my-claudecode: Precedence Rules Explained

> Understand oh-my-claudecode configuration precedence. Learn how project-scoped, global, and environment variables merge, with project settings overriding global ones.

- Repository: [Bellman/oh-my-claudecode](https://github.com/Yeachan-Heo/oh-my-claudecode)
- Tags: deep-dive
- Published: 2026-03-27

---

**Project-scoped configuration in `oh-my-claudecode` takes precedence over global user settings, while environment variables override both file-based configurations according to the merge logic in [`src/config/loader.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/config/loader.ts).**

Understanding how `oh-my-claudecode` resolves conflicting settings between your global preferences and project-specific requirements is essential for effective CLI workflow management. This open-source tool loads configuration from multiple sources and resolves them through a deterministic merge strategy defined in the TypeScript source code.

## Configuration Sources and File Locations

`oh-my-claudecode` recognizes three distinct configuration sources, each targeting different scopes of operation. The system identifies these locations through the `getConfigPaths()` utility and loads them sequentially.

- **Global User Config**: Stored at `<user-config-dir>/claude-omc/config.jsonc` (typically `~/.config/claude-omc/config.jsonc`), this file contains your machine-wide default preferences.
- **Project Config**: Located at `<cwd>/.claude/omc.jsonc` relative to your current working directory, this file defines repository-specific overrides.
- **Environment Variables**: Any variable prefixed with `OMC_` that maps to a configuration key, providing temporary, session-based overrides.

## The Configuration Precedence Hierarchy

The resolution follows a cascading override pattern where later sources overwrite earlier ones. As implemented in the `loadConfig()` function within [`src/config/loader.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/config/loader.ts), the effective precedence from highest to lowest is:

1. Environment variables
2. Project-scoped configuration (`.claude/omc.jsonc`)
3. Global user configuration (`~/.config/claude-omc/config.jsonc`)
4. Built-in defaults

### Built-in Defaults (Base Layer)

The loader initializes every configuration object using `buildDefaultConfig()`, which establishes safe fallback values for all settings. These defaults persist unless explicitly overridden by higher-precedence sources.

### Global User Configuration

The global config file applies across all projects on your machine. During initialization, the loader calls `loadJsoncFile(paths.user)` to parse this file and merges it into the defaults using `deepMerge()`.

### Project-Scoped Configuration

Settings defined in `./.claude/omc.jsonc` take precedence over matching keys in your global configuration. The loader executes `deepMerge(config, projectConfig)` after processing the user config, ensuring project-specific requirements override personal defaults.

### Environment Variables (Highest Precedence)

Variables such as `OMC_PARALLEL_EXECUTION` trump both configuration files. The `loadEnvConfig()` function captures these values and performs a final `deepMerge()`, allowing temporary overrides without modifying files.

## How the Merge Logic Works in src/config/loader.ts

The actual implementation in [`src/config/loader.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/config/loader.ts) demonstrates this precedence through an explicit four-step sequence:

```typescript
// 1. defaults (already in `config`)
let config = buildDefaultConfig();

// 2. user (global) config – lower precedence
const userConfig = loadJsoncFile(paths.user);
if (userConfig) {
  config = deepMerge(config, userConfig);
}

// 3. project config – *higher* precedence than user config
const projectConfig = loadJsoncFile(paths.project);
if (projectConfig) {
  config = deepMerge(config, projectConfig);
}

// 4. environment variables – highest precedence
const envConfig = loadEnvConfig();
config = deepMerge(config, envConfig);

```

This sequential merging ensures that each layer can only override what came before it, never reverting to defaults unless explicitly undefined in the new layer.

## Practical Configuration Examples

### Project Settings Overriding Global Preferences

Consider a scenario where you disable parallel execution globally but enable it for a specific repository:

```json5
// ~/.config/claude-omc/config.jsonc
{
  "features": {
    "parallelExecution": false
  }
}

```

```json5
// ./.claude/omc.jsonc (project-scoped)
{
  "features": {
    "parallelExecution": true
  }
}

```

When `loadConfig()` executes, the project file's `true` value replaces the global `false` setting.

### Environment Variables Overriding Files

To temporarily disable the feature regardless of file configurations:

```bash
export OMC_PARALLEL_EXECUTION=false

```

This environment variable forces the setting to `false`, overriding both the project and global configurations.

## Summary

- **Project-scoped configuration** in `.claude/omc.jsonc` takes precedence over global user settings in `~/.config/claude-omc/config.jsonc`.
- **Environment variables** with the `OMC_` prefix override all file-based configurations.
- The merge logic in [`src/config/loader.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/config/loader.ts) processes sources sequentially: defaults → global → project → environment.
- Missing keys in higher-precedence sources fall back to values defined in lower-precedence layers.

## Frequently Asked Questions

### Can I disable project-scoped configuration entirely?

No, the loader always checks for `./.claude/omc.jsonc` if it exists in your current working directory. However, you can prevent overrides by ensuring the project file contains only empty objects or keys you don't mind being overridden.

### What happens if the project config file is malformed?

If `loadJsoncFile(paths.project)` encounters invalid JSONC syntax, the loader typically skips that layer and falls back to the global user configuration or defaults, depending on the error handling implemented in [`src/utils/jsonc.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/utils/jsonc.ts).

### Do environment variables support nested configuration keys?

Yes, variables like `OMC_FEATURES_PARALLEL_EXECUTION` map to nested objects (`features.parallelExecution`) during the `loadEnvConfig()` processing, allowing you to override deeply nested settings without creating config files.

### Where does oh-my-claudecode store its built-in defaults?

The default configuration values are defined in the `buildDefaultConfig()` function within [`src/config/loader.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/config/loader.ts), supplemented by model defaults in [`src/config/models.ts`](https://github.com/Yeachan-Heo/oh-my-claudecode/blob/main/src/config/models.ts).