55
66import os
77from dataclasses import dataclass , field
8+
89from dotenv import load_dotenv
910
1011load_dotenv ()
@@ -34,75 +35,163 @@ def _env_float(name: str, default: float) -> float:
3435@dataclass (frozen = True )
3536class ModelConfig :
3637 """LLM model settings."""
37- name : str = field (default_factory = lambda : os .getenv ("DEVMIND_MODEL" , "claude-sonnet-4-6" ))
38- max_tokens : int = field (default_factory = lambda : _env_int ("DEVMIND_MAX_TOKENS" , 4096 ))
38+
39+ name : str = field (
40+ default_factory = lambda : os .getenv ("DEVMIND_MODEL" , "claude-sonnet-4-6" )
41+ )
42+ max_tokens : int = field (
43+ default_factory = lambda : _env_int ("DEVMIND_MAX_TOKENS" , 4096 )
44+ )
3945 streaming : bool = True
4046
4147
4248@dataclass (frozen = True )
4349class RetryConfig :
4450 """Retry logic settings with exponential backoff + jitter."""
51+
4552 max_retries : int = field (default_factory = lambda : _env_int ("DEVMIND_MAX_RETRIES" , 4 ))
46- base_delay : float = field (default_factory = lambda : _env_float ("DEVMIND_RETRY_BASE_DELAY" , 1.0 ))
47- max_delay : float = field (default_factory = lambda : _env_float ("DEVMIND_RETRY_MAX_DELAY" , 30.0 ))
53+ base_delay : float = field (
54+ default_factory = lambda : _env_float ("DEVMIND_RETRY_BASE_DELAY" , 1.0 )
55+ )
56+ max_delay : float = field (
57+ default_factory = lambda : _env_float ("DEVMIND_RETRY_MAX_DELAY" , 30.0 )
58+ )
4859 backoff_factor : float = 2.0
4960 jitter : float = 0.25
5061 retryable_codes : tuple = (
51- "529" , "overloaded" , "rate_limit" ,
52- "timeout" , "connection" , "502" , "503" , "504" ,
53- "ECONNRESET" , "Temporary failure" ,
62+ "529" ,
63+ "overloaded" ,
64+ "rate_limit" ,
65+ "timeout" ,
66+ "connection" ,
67+ "502" ,
68+ "503" ,
69+ "504" ,
70+ "ECONNRESET" ,
71+ "Temporary failure" ,
5472 )
5573
5674
5775@dataclass (frozen = True )
5876class RateLimitConfig :
5977 """Client-side rate limiting (token bucket)."""
78+
6079 enabled : bool = field (default_factory = lambda : _env_bool ("DEVMIND_RATE_LIMIT" , True ))
61- requests_per_minute : int = field (default_factory = lambda : _env_int ("DEVMIND_RPM" , 50 ))
80+ requests_per_minute : int = field (
81+ default_factory = lambda : _env_int ("DEVMIND_RPM" , 50 )
82+ )
6283 burst : int = field (default_factory = lambda : _env_int ("DEVMIND_BURST" , 10 ))
6384
6485
6586@dataclass (frozen = True )
6687class ToolConfig :
6788 """Tool execution settings."""
68- bash_timeout : int = field (default_factory = lambda : _env_int ("DEVMIND_BASH_TIMEOUT" , 30 ))
89+
90+ bash_timeout : int = field (
91+ default_factory = lambda : _env_int ("DEVMIND_BASH_TIMEOUT" , 30 )
92+ )
6993 file_read_max_lines : int = 200
7094 file_write_max_bytes : int = 5 * 1024 * 1024
7195 file_read_max_bytes : int = 2 * 1024 * 1024
7296 grep_max_results : int = 20
7397 grep_max_files : int = 5000
7498 blocked_commands : tuple = (
75- "rm -rf /" , "rm -rf /*" , "rm -rf ~" , "rm -rf ~/" ,
76- "mkfs" , "dd if=" , "dd of=/dev/" ,
77- ":(){" , "fork bomb" , "chmod -R 777 /" ,
78- "wget " , "curl " ,
79- "shutdown" , "reboot" , "halt" , "poweroff" ,
80- "> /dev/sda" , "mv / " ,
99+ "rm -rf /" ,
100+ "rm -rf /*" ,
101+ "rm -rf ~" ,
102+ "rm -rf ~/" ,
103+ "mkfs" ,
104+ "dd if=" ,
105+ "dd of=/dev/" ,
106+ ":(){" ,
107+ "fork bomb" ,
108+ "chmod -R 777 /" ,
109+ "wget " ,
110+ "curl " ,
111+ "shutdown" ,
112+ "reboot" ,
113+ "halt" ,
114+ "poweroff" ,
115+ "> /dev/sda" ,
116+ "mv / " ,
117+ )
118+ text_extensions : frozenset = frozenset (
119+ {
120+ ".py" ,
121+ ".js" ,
122+ ".ts" ,
123+ ".jsx" ,
124+ ".tsx" ,
125+ ".java" ,
126+ ".c" ,
127+ ".cpp" ,
128+ ".h" ,
129+ ".cs" ,
130+ ".go" ,
131+ ".rs" ,
132+ ".rb" ,
133+ ".php" ,
134+ ".swift" ,
135+ ".kt" ,
136+ ".scala" ,
137+ ".html" ,
138+ ".css" ,
139+ ".scss" ,
140+ ".less" ,
141+ ".xml" ,
142+ ".json" ,
143+ ".yaml" ,
144+ ".yml" ,
145+ ".toml" ,
146+ ".ini" ,
147+ ".cfg" ,
148+ ".conf" ,
149+ ".env" ,
150+ ".sh" ,
151+ ".bash" ,
152+ ".zsh" ,
153+ ".md" ,
154+ ".txt" ,
155+ ".rst" ,
156+ ".csv" ,
157+ ".sql" ,
158+ ".r" ,
159+ ".R" ,
160+ ".lua" ,
161+ ".dockerfile" ,
162+ ".makefile" ,
163+ ".gitignore" ,
164+ ".editorconfig" ,
165+ }
166+ )
167+ skip_dirs : frozenset = frozenset (
168+ {
169+ ".git" ,
170+ "__pycache__" ,
171+ "node_modules" ,
172+ ".venv" ,
173+ "venv" ,
174+ ".mypy_cache" ,
175+ ".pytest_cache" ,
176+ "dist" ,
177+ "build" ,
178+ ".tox" ,
179+ }
81180 )
82- text_extensions : frozenset = frozenset ({
83- ".py" , ".js" , ".ts" , ".jsx" , ".tsx" , ".java" , ".c" , ".cpp" , ".h" ,
84- ".cs" , ".go" , ".rs" , ".rb" , ".php" , ".swift" , ".kt" , ".scala" ,
85- ".html" , ".css" , ".scss" , ".less" , ".xml" , ".json" , ".yaml" , ".yml" ,
86- ".toml" , ".ini" , ".cfg" , ".conf" , ".env" , ".sh" , ".bash" , ".zsh" ,
87- ".md" , ".txt" , ".rst" , ".csv" , ".sql" , ".r" , ".R" , ".lua" ,
88- ".dockerfile" , ".makefile" , ".gitignore" , ".editorconfig" ,
89- })
90- skip_dirs : frozenset = frozenset ({
91- ".git" , "__pycache__" , "node_modules" , ".venv" , "venv" ,
92- ".mypy_cache" , ".pytest_cache" , "dist" , "build" , ".tox" ,
93- })
94181
95182
96183@dataclass (frozen = True )
97184class HistoryConfig :
98185 """Conversation history settings."""
186+
99187 max_messages : int = 20
100188 summary_threshold : int = 16
101189
102190
103191@dataclass (frozen = True )
104192class PersistenceConfig :
105193 """Conversation save/load settings."""
194+
106195 enabled : bool = field (default_factory = lambda : _env_bool ("DEVMIND_PERSIST" , True ))
107196 sessions_dir : str = field (
108197 default_factory = lambda : os .getenv (
@@ -116,13 +205,15 @@ class PersistenceConfig:
116205@dataclass (frozen = True )
117206class MetricsConfig :
118207 """Performance metrics tracking."""
208+
119209 enabled : bool = field (default_factory = lambda : _env_bool ("DEVMIND_METRICS" , True ))
120210 persist_on_exit : bool = True
121211
122212
123213@dataclass (frozen = True )
124214class PluginConfig :
125215 """Plugin/extension system settings."""
216+
126217 enabled : bool = field (default_factory = lambda : _env_bool ("DEVMIND_PLUGINS" , True ))
127218 plugins_dir : str = field (
128219 default_factory = lambda : os .getenv (
@@ -135,6 +226,7 @@ class PluginConfig:
135226@dataclass (frozen = True )
136227class AppConfig :
137228 """Master application configuration."""
229+
138230 model : ModelConfig = field (default_factory = ModelConfig )
139231 retry : RetryConfig = field (default_factory = RetryConfig )
140232 rate_limit : RateLimitConfig = field (default_factory = RateLimitConfig )
0 commit comments