How Feynman Loads Environment Variables from .env Files: Native CLI Implementation
Feynman loads environment variables from .env files using a custom-built parser in src/cli.ts that synchronously reads the file, parses key-value pairs while handling quotes and comments, and injects them into process.env before any application modules initialize.
Unlike many Node.js CLIs that rely on the external dotenv package, the advaitpaliwal/feynman repository implements its own lightweight loader directly in the entry point. This native approach eliminates a production dependency—documented in the [CHANGELOG.md](https://github.com/advaitpaliwal/feynman/blob/main/CHANGELOG.md)—while ensuring API keys and configuration values are available immediately when the feynman command executes.
The Native .env Loader in src/cli.ts
The environment loading logic lives entirely within the CLI entry point at src/cli.ts. When you invoke the feynman command, the loader executes before any other imports, guaranteeing that process.env contains your local configuration before Pi sub-agents or workbench components start.
File Detection and Synchronous Reading
The loader first resolves the target path using the current working directory:
import { readFileSync, existsSync } from 'node:fs';
import { resolve } from 'node:path';
const envPath = resolve(process.cwd(), '.env');
if (existsSync(envPath)) {
const raw = readFileSync(envPath, 'utf8');
// ... parsing logic
}
This synchronous approach ensures that environment variables are fully populated before any asynchronous module loading begins.
Parsing Logic and Value Sanitization
For each line in the raw file content, Feynman applies a strict parsing protocol:
- Blank lines and lines beginning with
#are ignored - Each valid line is split on the first
=character only (indexOf('=')) - Both keys and values are trimmed of surrounding whitespace
- Surrounding quotes—whether
",', or`—are stripped from values - Escaped sequences such as
\nare processed during parsing
This implementation mirrors standard dotenv behavior but handles the processing natively without external libraries.
Conditional Injection into process.env
Crucially, Feynman respects pre-existing environment variables:
if (process.env[key] === undefined) {
process.env[key] = value;
}
Variables are assigned only if they are not already defined, ensuring that system-level exports or command-line overrides take precedence over .env file values.
Why Feynman Dropped the dotenv Dependency
According to the repository's release notes, the external dotenv package was removed in recent releases. The native implementation provides three advantages:
- Zero dependency overhead for configuration loading
- Faster cold-start times by avoiding an extra require() resolution
- Predictable behavior baked directly into the CLI bootstrap sequence
Complete Loader Implementation
Here is the simplified parsing logic as implemented in src/cli.ts:
import { readFileSync, existsSync } from 'node:fs';
import { resolve } from 'node:path';
const envPath = resolve(process.cwd(), '.env');
if (existsSync(envPath)) {
const raw = readFileSync(envPath, 'utf8');
raw.split('\n').forEach(line => {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) return;
const idx = trimmed.indexOf('=');
if (idx === -1) return;
const key = trimmed.slice(0, idx).trim();
let value = trimmed.slice(idx + 1).trim();
// Strip surrounding quotes if present
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'")) ||
(value.startsWith('`') && value.endsWith('`'))
) {
value = value.slice(1, -1);
}
if (process.env[key] === undefined) {
process.env[key] = value;
}
});
}
Key Files in the Execution Chain
Understanding the full execution flow requires examining three specific files:
bin/feynman.js– The executable wrapper that invokes the CLI and triggers the loadersrc/cli.ts– Contains the native.envparsing logic that runs before any other codesrc/workbench/settings-store.ts– Example consumer module that reads the injected variables after initialization
If the .env file is missing from process.cwd(), the loader silently skips initialization without throwing errors or warnings.
Summary
- Feynman implements a native .env parser in
src/cli.tsrather than using thedotenvnpm package - The loader runs synchronously before module imports using
fs.readFileSyncandprocess.cwd() - Values are parsed by splitting on the first
=, trimming whitespace, and stripping surrounding quotes - Variables are injected into
process.envonly if undefined, preserving existing environment values - The implementation supports comments (
#), blank lines, and escaped sequences while maintaining zero external dependencies
Frequently Asked Questions
Does Feynman require a .env file to run?
No. The loader checks for file existence using existsSync and silently skips parsing if no .env file is present in the current working directory. The application will rely entirely on system environment variables or use default configurations where available.
What happens if a variable is already defined in the environment?
Feynman preserves pre-existing environment variables. During the injection phase, the code explicitly checks if (process.env[key] === undefined) before assignment, ensuring that shell-exported variables or Docker-injected secrets take precedence over values defined in the .env file.
Where is the .env loader implemented in the Feynman source code?
The loading logic is implemented directly in src/cli.ts, which serves as the main entry point for the CLI. This file is invoked by bin/feynman.js, and the environment setup occurs at the top of the module before any Pi sub-agents or workbench components are imported.
Does Feynman support multiline values in .env files?
The current implementation splits the file content on newline characters (\n), which means complex multiline values are not supported in the same way as the standard dotenv package. For configuration requiring multiline strings, users should rely on system environment variables or external configuration files handled after the CLI initializes.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →