Skip to content

feat(config): add OPENCLAUDE_CONFIG_DIR env var as preferred alias (#454)#935

Open
gnanam1990 wants to merge 1 commit intomainfrom
feat/issue-454-openclaude-config-dir
Open

feat(config): add OPENCLAUDE_CONFIG_DIR env var as preferred alias (#454)#935
gnanam1990 wants to merge 1 commit intomainfrom
feat/issue-454-openclaude-config-dir

Conversation

@gnanam1990
Copy link
Copy Markdown
Collaborator

Summary

Closes #454.

The legacy `CLAUDE_CONFIG_DIR` name was the only way to point openclaude at a non-default config home, which leaked Anthropic branding for a fork that has otherwise rebranded. This adds `OPENCLAUDE_CONFIG_DIR` as the preferred env var. `CLAUDE_CONFIG_DIR` continues to work for backward compatibility; when both are set with different values, `OPENCLAUDE_CONFIG_DIR` wins and a one-time warning is logged so users can clean up.

Quoting stepanovdg in the issue thread:

`export CLAUDE_CONFIG_DIR="$HOME/.openclaude"` is working — would be good to rename to `OPENCLAUDE_CONFIG_DIR` or whatever decided tool name.

Behavior

Env state Resolved config home
Only `OPENCLAUDE_CONFIG_DIR` set uses `OPENCLAUDE_CONFIG_DIR`
Only `CLAUDE_CONFIG_DIR` set (legacy) uses `CLAUDE_CONFIG_DIR`
Both set, same value uses that value (silent)
Both set, different values uses `OPENCLAUDE_CONFIG_DIR` + one-time warning
Neither set default `/.openclaude` (or `/.claude` for legacy installs that pre-date PR #280)

Conflict warning example:

`[openclaude] Both OPENCLAUDE_CONFIG_DIR and CLAUDE_CONFIG_DIR are set to different values. Using OPENCLAUDE_CONFIG_DIR=/path/a; ignoring CLAUDE_CONFIG_DIR=/path/b.`

Changes

  • `src/utils/envUtils.ts` — new `resolveConfigDirEnv()` helper picks `OPENCLAUDE_CONFIG_DIR` over `CLAUDE_CONFIG_DIR` and warns on conflict. Memoize cache key now tracks both env vars so changing either invalidates the cached result.
  • `src/utils/env.ts` — `getGlobalClaudeFile()` previously read `CLAUDE_CONFIG_DIR` directly (missing the new alias). Route through `resolveConfigDirEnv()` so the global config file path follows the same precedence.
  • `src/utils/secureStorage/macOsKeychainHelpers.ts` — the "is default dir" check used by keychain service-name scoping now considers both env vars.
  • `src/utils/swarm/spawnUtils.ts` — forward `OPENCLAUDE_CONFIG_DIR` to teammate processes alongside the legacy var.
  • `src/utils/openclaudePaths.test.ts` — +6 unit tests covering the new alias, fallthrough, conflict warning, and `resolveConfigDirEnv()` in isolation.
  • `.env.example` — document both env vars and the precedence rule.

Test plan

  • `bun run build` — passes (v0.7.0); built `dist/cli.mjs` includes the new resolver
  • `bun test src/utils/openclaudePaths.test.ts` — 16/16 pass (10 original + 6 new)
  • `bun test` (full) — 1639 pass; the 4 remaining failures (`StartupScreen.test.ts`, `thinking.test.ts`) reproduce on `main` and are unrelated
  • Memoization: cache invalidates across 4 sequential env transitions (OPENCLAUDE → OPENCLAUDE updated → switch to CLAUDE → both set)
  • Built CLI: `OPENCLAUDE_CONFIG_DIR=/tmp/x CLAUDE_CONFIG_DIR=/tmp/y node dist/cli.mjs` emits the conflict warning to stderr and resolves to `/tmp/x`
  • Path edges: NFC normalization, trailing slash preserved, spaces, empty-string fallthrough — all behave identically to legacy `CLAUDE_CONFIG_DIR`
  • Swarm forwarding: `buildInheritedEnvVars()` includes `OPENCLAUDE_CONFIG_DIR=...` in the line passed to teammate processes
  • Site audit: every direct `process.env.CLAUDE_CONFIG_DIR` read in src/ also checks `OPENCLAUDE_CONFIG_DIR` (verified by grep)

Notes for review

  • The conflict warning is intentionally non-spammy (once per process, via a module-level flag). Test escape hatch `__resetConfigDirEnvWarningForTesting` is exported but underscore-prefixed; only used by tests.
  • No red-flag rule hits: no `tengu_*`, no `USER_TYPE === 'ant'`, no new 3P network calls, no Anthropic fingerprints (this PR actively removes one), no Claude co-author trailer.
  • The default-dir behavior from fix: change default config dir from ~/.claude to ~/.openclaude #280 (`/.openclaude` for new installs, `/.claude` for legacy) is preserved unchanged.

…r CLAUDE_CONFIG_DIR (#454)

The legacy CLAUDE_CONFIG_DIR name was the only way to point openclaude
at a non-default config home, which leaked Anthropic branding for a
fork that has otherwise rebranded to OpenClaude. Add OPENCLAUDE_CONFIG_DIR
as the preferred name. CLAUDE_CONFIG_DIR continues to work for
backward compatibility; when both are set with different values,
OPENCLAUDE_CONFIG_DIR wins and a one-time warning is logged.

- src/utils/envUtils.ts: introduce resolveConfigDirEnv() that picks
  OPENCLAUDE_CONFIG_DIR over CLAUDE_CONFIG_DIR and emits a conflict
  warning. Memoize cache key now tracks both env vars so changing
  either invalidates the cached result.
- src/utils/env.ts: getGlobalClaudeFile() previously read
  CLAUDE_CONFIG_DIR directly, missing the new alias. Route through
  resolveConfigDirEnv() so the global config file path follows the
  same precedence.
- src/utils/secureStorage/macOsKeychainHelpers.ts: the "is default
  dir" check used by keychain service-name scoping now considers
  both env vars.
- src/utils/swarm/spawnUtils.ts: forward OPENCLAUDE_CONFIG_DIR to
  teammate processes alongside the legacy var.
- src/utils/openclaudePaths.test.ts: +6 unit tests covering the new
  alias, fallthrough, conflict warning, and resolveConfigDirEnv()
  in isolation.
- .env.example: document both env vars and the precedence rule.

Verified locally on Linux: with only OPENCLAUDE_CONFIG_DIR set, with
only CLAUDE_CONFIG_DIR set (legacy still works), with both set
matching (silent), with both set conflicting (warn once + OPENCLAUDE
wins), with neither set (default ~/.openclaude). Memo cache
invalidates across 4 sequential env transitions. Built dist/cli.mjs
honors the new var and emits the conflict warning to the user.
Copy link
Copy Markdown
Collaborator

@Vasanthdev2004 Vasanthdev2004 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I reviewed the current head and this looks good from my side.

Scope: Targeted review of the config-directory alias change and its direct call sites.

Verdict: Approve-ready

What I checked:

  • resolveConfigDirEnv() precedence: OPENCLAUDE_CONFIG_DIR wins, CLAUDE_CONFIG_DIR remains a legacy fallback, and conflict warnings are one-time.
  • getGlobalClaudeFile() now follows the same resolver instead of reading only the legacy env var.
  • Keychain service scoping and teammate env forwarding both consider the preferred and legacy config-dir env vars.
  • .env.example documents the preferred name, legacy alias, and precedence rule.
  • Ran bun test src/utils/openclaudePaths.test.ts — 16/16 passed.
  • Ran bun run build — passed.

I do not see a remaining blocker on the current head.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow change default settings dir, so that we could be able to change from ~/.claude to ~/.openclaude or whatever

3 participants