Skip to main content
nexlem

Nexlem environment variables

Overview

Nexlem reads several NEXLEM_* environment variables to redirect filesystem reads and writes (SQLite DB, campaigns directory, consumer config directory) and to locate the framework checkout. Specific path variables override the generic NEXLEM_PROJECT_DIR parent fallback, which in turn overrides the caller's projectDir argument (process.cwd() at the entry point).

This document is the single source of truth for env-var semantics in src/commands/run-paths.ts and src/lib/env.ts. Consumers (UAT scripts, headless claude -p workers, downstream orchestrators) MUST follow the precedence rules below.

Variables

NameTypePurposeDefaultRequired
NEXLEM_PROJECT_DIRabsolute or relative pathParent fallback: when set, DB/campaigns/config resolve under this dir(none — falls back to process.cwd())No
NEXLEM_DBabsolute pathSQLite database file location$NEXLEM_PROJECT_DIR/.nexlem/nexlem.dbNo
NEXLEM_CAMPAIGNS_DIRabsolute pathCampaigns base directory$NEXLEM_PROJECT_DIR/campaignsNo
NEXLEM_CONFIG_DIRabsolute pathConsumer config directory (config/)$NEXLEM_PROJECT_DIR/configNo
NEXLEM_FRAMEWORK_PATHabsolute pathOverride framework root (managed by Phase 49 / GLOBAL-04)Auto-detected from import.meta.urlNo

Defaults shown for the path variables collapse to <projectDir>/.nexlem/nexlem.db, <projectDir>/campaigns, and <projectDir>/config when NEXLEM_PROJECT_DIR is also unset (here <projectDir> is the argument passed to the resolver, typically process.cwd() at the entry point — see resolveProjectDir()).

NEXLEM_FRAMEWORK_PATH is owned by Phase 49 / GLOBAL-04 (framework-path resolution); it is listed here for completeness but is NOT modified by HEADLESS-05.

Precedence rules

For each path category, the resolvers evaluate sources in this order and return the first one that is set:

  • DB (resolveDbPath): NEXLEM_DBNEXLEM_PROJECT_DIR/.nexlem/nexlem.db<projectDir>/.nexlem/nexlem.db
  • Campaigns dir (resolveCampaignsBaseDir): NEXLEM_CAMPAIGNS_DIRNEXLEM_PROJECT_DIR/campaigns<projectDir>/campaigns
  • Config dir (resolveConfigDir): NEXLEM_CONFIG_DIRNEXLEM_PROJECT_DIR/config<projectDir>/config
  • Project root (resolveProjectDir): explicit caller arg → NEXLEM_PROJECT_DIRprocess.cwd()

The CLI flag --project-dir (HEADLESS-04) overrides ALL env-derived projectDir values when present — it becomes the explicit caller arg passed through resolveProjectDir() and downstream resolvers. Specific env vars (NEXLEM_DB / NEXLEM_CAMPAIGNS_DIR / NEXLEM_CONFIG_DIR) still take precedence over the CLI flag because they target a single path, not a parent root — this matches the "specific overrides general" principle.

Note (HEADLESS-05): resolveCampaignDir(projectDir, configPath) (the campaign-specific lookup that re-roots a stored campaign.config_path) is intentionally NOT extended to honor NEXLEM_PROJECT_DIR. That value is a persisted DB field whose anchor must remain stable across env-var changes; see the JSDoc in src/commands/run-paths.ts for the full rationale.

Worked examples

Case A — pure parent fallback. Only NEXLEM_PROJECT_DIR is set:

NEXLEM_PROJECT_DIR=/tmp/test bun run src/main.ts campaign list
# DB:        /tmp/test/.nexlem/nexlem.db
# Campaigns: /tmp/test/campaigns
# Config:    /tmp/test/config

Case B — specific override wins over parent. Both NEXLEM_PROJECT_DIR and NEXLEM_DB are set; NEXLEM_DB wins for the DB, the rest still inherit from NEXLEM_PROJECT_DIR:

NEXLEM_PROJECT_DIR=/tmp/test NEXLEM_DB=/tmp/override.db \
  bun run src/main.ts campaign list
# DB:        /tmp/override.db          (NEXLEM_DB wins — specific)
# Campaigns: /tmp/test/campaigns       (inherited from NEXLEM_PROJECT_DIR)
# Config:    /tmp/test/config          (inherited from NEXLEM_PROJECT_DIR)

Case C — CLI flag wins for project root. --project-dir overrides the env var:

bun run src/main.ts campaign list --project-dir /tmp/test
# DB:        /tmp/test/.nexlem/nexlem.db
# Campaigns: /tmp/test/campaigns
# Config:    /tmp/test/config

Relative vs absolute paths (asymmetry)

NEXLEM_PROJECT_DIR (and the other path env vars) accept both absolute and relative paths. Relative paths are resolved against the current working directory at the moment the resolver is invoked (Node's path.join semantics).

The --project-dir CLI flag (HEADLESS-04) is stricter: it rejects relative paths at parse time and exits non-zero. This is a defensive measure against argv injection in headless wrappers, where untrusted callers may construct argv strings programmatically.

Rationale. Env vars are set at shell level by the user, where interactive ergonomics favor leniency — typing NEXLEM_PROJECT_DIR=./sandbox from a working directory you control is a reasonable affordance. CLI argv may be constructed by automated wrappers where strictness is safer — a relative --project-dir value injected by an upstream tool could expand to an unexpected location depending on the worker's spawn cwd.

Operational guidance: in headless contexts (claude -p workers, CI agents, automated batches) always pass absolute paths via --project-dir. The env-var leniency is a developer-ergonomics affordance for interactive use and should NOT be relied on in production worker harnesses.

Forward-compatibility note

v2.4 headless invocations set NEXLEM_PROJECT_DIR once per spawn; specific env vars remain available for UAT isolation and per-path overrides (e.g. pointing the DB at a temporary location while keeping campaigns/config on the project root).

Traceability

Implemented under HEADLESS-05 (Phase 50). Resolver source: src/commands/run-paths.ts. Tests: src/commands/run-paths.test.ts (plus the four existing call-site regressions in tests/unit/run-paths-resolve-campaign-dir.test.ts, tests/unit/run-nexlem-db-override.test.ts, tests/unit/run-config-dir-override.test.ts, and tests/integration/config-path-env-override.test.ts).

NEXLEM_FRAMEWORK_PATH schema and runtime resolution: Phase 49 / GLOBAL-04 (src/lib/env.ts and src/lib/framework-path.ts). Not modified by this plan.

Edit this page on GitHub