-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathsettings.py
More file actions
113 lines (91 loc) · 3.59 KB
/
settings.py
File metadata and controls
113 lines (91 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
"""Shared settings loading utilities."""
import json
import os
from pathlib import Path
from typing import Dict, Tuple
# Use absolute imports for bridge compatibility
try:
from .constants import (
USER_SETTINGS_FILE,
PROJECT_SETTINGS_DIR,
SETTINGS_FILE,
MCP_CONFIG_FILE,
USER_PROFILES_DIR,
PROFILES_FILE
)
from .error_handler import safe_json_load
except ImportError:
# Fallback for standalone bridge script
from constants import (
USER_SETTINGS_FILE,
PROJECT_SETTINGS_DIR,
SETTINGS_FILE,
MCP_CONFIG_FILE,
USER_PROFILES_DIR,
PROFILES_FILE
)
from error_handler import safe_json_load
def load_project_settings(cwd: str = None) -> dict:
"""Load and merge user-level and project settings.
User settings from ~/.claude.json are loaded first,
then project settings (.claude/settings.json) override them.
"""
# Start with user-level settings
user_settings = safe_json_load(str(USER_SETTINGS_FILE), default={})
if not cwd:
return user_settings
# Load project settings
# Try .claude/settings.json first
settings_path = os.path.join(cwd, PROJECT_SETTINGS_DIR, SETTINGS_FILE)
project_settings = safe_json_load(settings_path, default={})
# Try .mcp.json (MCP servers only) if no project settings
if not project_settings:
mcp_path = os.path.join(cwd, MCP_CONFIG_FILE)
project_settings = safe_json_load(mcp_path, default={})
# Load settings.local.json (Claude CLI's native local settings)
local_path = os.path.join(cwd, PROJECT_SETTINGS_DIR, "settings.local.json")
local_settings = safe_json_load(local_path, default={})
# Merge: user < project < local
result = merge_settings(user_settings, project_settings)
result = merge_settings(result, local_settings)
# Merge permissions.allow into autoAllowedMcpTools (Claude CLI format → our format)
permissions_allow = result.get("permissions", {}).get("allow", [])
if permissions_allow:
auto_allowed = result.get("autoAllowedMcpTools", [])
existing = set(auto_allowed)
for pattern in permissions_allow:
if pattern not in existing:
auto_allowed.append(pattern)
result["autoAllowedMcpTools"] = auto_allowed
return result
def merge_settings(user: dict, project: dict) -> dict:
"""Deep merge project settings into user settings."""
result = user.copy()
for key, value in project.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
# Deep merge dictionaries
result[key] = {**result[key], **value}
else:
# Project value overrides user value
result[key] = value
return result
def load_profiles_and_checkpoints(project_path: str = None) -> Tuple[Dict, Dict]:
"""Load profiles and checkpoints with cascade: user < project.
Args:
project_path: Optional project-specific profiles path
Returns:
Tuple of (profiles dict, checkpoints dict)
"""
profiles = {}
checkpoints = {}
# Load user-level
user_profiles_path = USER_PROFILES_DIR / PROFILES_FILE
data = safe_json_load(str(user_profiles_path), default={})
profiles.update(data.get("profiles", {}))
checkpoints.update(data.get("checkpoints", {}))
# Load project-level (overrides user)
if project_path:
data = safe_json_load(project_path, default={})
profiles.update(data.get("profiles", {}))
checkpoints.update(data.get("checkpoints", {}))
return profiles, checkpoints