# Environment Variable Precedence in Oh-My-Pi (omp): The Complete Resolution Order

> Understand Oh-My-Pi environment variable precedence. Discover the five-level hierarchy and how .env files override others for complete resolution control.

- Repository: [Can Bölük/oh-my-pi](https://github.com/can1357/oh-my-pi)
- Tags: how-to-guide
- Published: 2026-05-21

---

**Oh-My-Pi (omp) resolves environment variables using a strict five-level precedence hierarchy where project-level `.env` files override agent, global, and home directory configurations, with existing process environment variables serving as the final fallback.**

The `omp` (Oh-My-Pi) toolchain normalizes environment variable resolution through a centralized loading system implemented in the `@oh-my-pi/pi-utils` package. Understanding the precedence order for resolving environment variables in omp is critical for debugging configuration conflicts across multi-directory setups. The resolution logic, found in [`packages/utils/src/env.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/utils/src/env.ts), employs a "first-source-wins" merging strategy that prioritizes local project definitions over global settings.

## The Five-Level Precedence Hierarchy

When the `env` module initializes, it loads and merges environment variables from four distinct `.env` file locations in a specific sequence. The effective precedence from highest to lowest priority is:

1. **Project `.env`** – Located in the current working directory where `omp` is executed (`process.cwd() + "/.env"`)
2. **Agent `.env`** – Located in the tool's own data directory (`getAgentDir() + "/.env"`)
3. **Global π `.env`** – Located in the shared configuration directory (`getConfigRootDir() + "/.env"`)
4. **User-home `.env`** – Located in the user's home directory (`os.homedir() + "/.env"`)
5. **Existing process environment** – Variables already present in `Bun.env` before the module runs

Sources at the top of this list take precedence over those below. If a variable is defined in both the project `.env` and the home directory `.env`, the project value wins because it is merged first.

## How the Resolution Logic Works

The core resolution mechanism operates in [`packages/utils/src/env.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/utils/src/env.ts) through a synchronous loading phase followed by a conditional merge phase.

First, the system loads all four `.env` files simultaneously (lines 88-93):

```typescript
// Synchronous loading from four locations
const projectEnv = parseEnvFile(process.cwd() + "/.env");
const agentEnv = parseEnvFile(getAgentDir() + "/.env");
const piEnv = parseEnvFile(getConfigRootDir() + "/.env");
const homeEnv = parseEnvFile(os.homedir() + "/.env");

```

Next, the merging algorithm processes these sources in a fixed array order (lines 101-107):

```typescript
// Merging loop: first source wins
for (const env of [projectEnv, agentEnv, piEnv, homeEnv]) {
  for (const [key, value] of Object.entries(env)) {
    if (!(key in Bun.env)) {
      Bun.env[key] = value;
    }
  }
}

```

The critical logic is the condition `if (!(key in Bun.env))`. This check ensures that once a variable is set by a higher-priority source, lower-priority sources cannot overwrite it. Consequently, **the array order `[projectEnv, agentEnv, piEnv, homeEnv]` directly determines the precedence hierarchy**.

## Safety Filtering and Variable Aliasing

Before merging occurs, the module applies security filtering and namespace aliasing rules.

**Safety filtering** (lines 33-40 and 94-99) validates environment variable names and values, stripping any entries with invalid characters or dangerous patterns before they enter the `Bun.env` object. This prevents injection attacks through malformed `.env` files.

**OMP_ to PI_ aliasing** (lines 78-83) automatically duplicates variables for backward compatibility. After parsing any `.env` file, if a key starts with `OMP_`, the system creates an additional entry with the `PI_` prefix. For example, `OMP_API_TOKEN` becomes accessible as both `OMP_API_TOKEN` and `PI_API_TOKEN`.

Finally, the module re-exports `Bun.env` as `$env` (lines 115-117), making the fully resolved environment available to the rest of the application.

## Accessing Resolved Variables in Code

Once the precedence resolution completes, you can access variables through the `@oh-my-pi/pi-utils` package:

```typescript
// Import triggers the loading/merging logic immediately
import { $env, $pickenv, $flag } from "@oh-my-pi/pi-utils";

// Direct access reflects the final merged environment
console.log("API token:", $env["OMP_API_TOKEN"]);   // Also accessible as $env["PI_API_TOKEN"]

// Resolve the first defined value among alternatives
const apiKey = $pickenv("OMP_API_TOKEN", "PI_API_TOKEN", "MY_CUSTOM_TOKEN");
if (!apiKey) throw new Error("No API token found in any scope");

// Check boolean flags respecting the same precedence
if ($flag("OMP_DEBUG")) {
  console.log("Debug mode enabled via highest-precedence source");
}

```

The `$pickenv` helper searches through the provided keys in order, while `$flag` normalizes truthy values (`"1"`, `"true"`, `"yes"`) to boolean `true` according to the resolved precedence hierarchy.

## Summary

- **Project `.env` files hold the highest priority**, overriding agent, global, and home configurations due to the `[projectEnv, agentEnv, piEnv, homeEnv]` merge order in [`packages/utils/src/env.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/utils/src/env.ts) (lines 101-107).
- **First-source-wins logic** prevents lower-priority sources from overwriting variables already set by higher-priority sources.
- **Safety filtering** occurs before merging (lines 33-40), stripping invalid entries to prevent environment pollution.
- **Automatic aliasing** creates `PI_` prefixed copies of any `OMP_` variables (lines 78-83).
- **Process environment variables** serve as the lowest priority layer, only populating `Bun.env` if not defined in any `.env` file (lines 94-99).

## Frequently Asked Questions

### What happens if the same variable is defined in both project and home .env files?

The project `.env` value takes precedence. According to the merge loop in [`packages/utils/src/env.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/utils/src/env.ts) (lines 101-107), project environment variables are processed first, so when the home directory `.env` is merged later, the condition `if (!(key in Bun.env))` evaluates to false, and the home directory value is ignored.

### How does omp handle environment variables prefixed with OMP_ versus PI_?

After parsing any `.env` file, omp automatically creates aliases for compatibility. Any key starting with `OMP_` is duplicated with a `PI_` prefix (lines 78-83 in [`env.ts`](https://github.com/can1357/oh-my-pi/blob/main/env.ts)). This means defining `OMP_API_KEY` in your project `.env` makes it accessible via both `$env["OMP_API_KEY"]` and `$env["PI_API_KEY"]`, with both references pointing to the same resolved value.

### Can existing system environment variables override .env file configurations?

No. Existing process environment variables (already present in `Bun.env` when the module loads) are filtered through a safety check (lines 94-99) but are not overwritten by `.env` file contents. However, if a variable is *not* defined in the process environment but exists in multiple `.env` files, the project-level definition wins over agent, global, and home definitions.

### Where does omp look for .env files by default?

Omp searches four specific locations in order of precedence: the current working directory (`process.cwd()`), the agent data directory (`getAgentDir()`), the global configuration root (`getConfigRootDir()`), and the user's home directory (`os.homedir()`). These paths are resolved in [`packages/utils/src/env.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/utils/src/env.ts) (lines 88-93) using helper functions from [`packages/utils/src/dirs.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/utils/src/dirs.ts).