Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/ucode/agents/claude.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
APP_DIR,
ToolSpec,
backup_existing_file,
claude_config_dir,
deep_merge_dict,
read_json_safe,
write_json_file,
Expand All @@ -40,7 +41,7 @@
from ucode.tracing import tracing_env
from ucode.ui import print_err, print_note, print_success, print_warning

CLAUDE_CONFIG_DIR = Path.home() / ".claude"
CLAUDE_CONFIG_DIR = claude_config_dir()
CLAUDE_SETTINGS_PATH = CLAUDE_CONFIG_DIR / "ucode-settings.json"
CLAUDE_BACKUP_PATH = APP_DIR / "claude-ucode-settings.backup.json"

Expand Down
8 changes: 8 additions & 0 deletions src/ucode/config_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import json
import os
from pathlib import Path
from typing import TypedDict, cast

Expand All @@ -22,6 +23,13 @@ class ToolSpec(TypedDict):

APP_DIR = Path.home() / ".ucode"


def claude_config_dir() -> Path:
"""Claude Code's user config directory: ``$CLAUDE_CONFIG_DIR`` or ``~/.claude``."""
override = os.environ.get("CLAUDE_CONFIG_DIR", "").strip()
return Path(override).expanduser() if override else Path.home() / ".claude"
Comment on lines +27 to +30


_dry_run = False


Expand Down
27 changes: 27 additions & 0 deletions tests/test_config_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import ucode.config_io as config_io
from ucode.config_io import (
backup_existing_file,
claude_config_dir,
deep_merge_dict,
ensure_parent_dir,
is_dry_run,
Expand Down Expand Up @@ -373,3 +374,29 @@ def test_partial_path_through_non_dict_is_noop(self):
doc = {"model": "scalar"}
assert prune_key_paths(doc, [["model", "nested"]]) is False
assert doc == {"model": "scalar"}


# ---------------------------------------------------------------------------
# claude_config_dir
# ---------------------------------------------------------------------------


class TestClaudeConfigDir:
def test_defaults_to_home_dot_claude(self, tmp_path, monkeypatch):
monkeypatch.delenv("CLAUDE_CONFIG_DIR", raising=False)
monkeypatch.setattr(config_io.Path, "home", classmethod(lambda cls: tmp_path))
assert claude_config_dir() == tmp_path / ".claude"

def test_env_var_overrides_default(self, tmp_path, monkeypatch):
monkeypatch.setenv("CLAUDE_CONFIG_DIR", str(tmp_path / "work"))
assert claude_config_dir() == tmp_path / "work"

def test_env_var_expands_tilde(self, tmp_path, monkeypatch):
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("CLAUDE_CONFIG_DIR", "~/claude-work")
assert claude_config_dir() == tmp_path / "claude-work"

def test_blank_env_var_falls_back_to_default(self, tmp_path, monkeypatch):
monkeypatch.setenv("CLAUDE_CONFIG_DIR", " ")
monkeypatch.setattr(config_io.Path, "home", classmethod(lambda cls: tmp_path))
assert claude_config_dir() == tmp_path / ".claude"