janito 0.14.0__py3-none-any.whl → 0.15.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ """
2
+ Core configuration components for Janito.
3
+ Provides the base Config class and related functionality.
4
+ """
5
+ from .singleton import Config
6
+ from .properties import ConfigProperties
7
+ from .file_operations import (
8
+ get_global_config_path,
9
+ get_local_config_path,
10
+ load_config_file,
11
+ save_config_file,
12
+ merge_configs
13
+ )
14
+
15
+ __all__ = [
16
+ "Config",
17
+ "ConfigProperties",
18
+ "get_global_config_path",
19
+ "get_local_config_path",
20
+ "load_config_file",
21
+ "save_config_file",
22
+ "merge_configs"
23
+ ]
@@ -0,0 +1,90 @@
1
+ """
2
+ File operations for configuration management.
3
+ """
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Dict, Any, Optional
7
+
8
+ def get_global_config_path() -> Path:
9
+ """
10
+ Get the path to the global configuration file.
11
+
12
+ Returns:
13
+ Path object pointing to the global configuration file (~/.janito/config.json)
14
+ """
15
+ return Path.home() / ".janito" / "config.json"
16
+
17
+ def get_local_config_path(workspace_dir: str) -> Path:
18
+ """
19
+ Get the path to the local configuration file.
20
+
21
+ Args:
22
+ workspace_dir: Current workspace directory
23
+
24
+ Returns:
25
+ Path object pointing to the local configuration file (.janito/config.json)
26
+ """
27
+ return Path(workspace_dir) / ".janito" / "config.json"
28
+
29
+ def load_config_file(config_path: Path) -> Dict[str, Any]:
30
+ """
31
+ Load configuration from a file.
32
+
33
+ Args:
34
+ config_path: Path to the configuration file
35
+
36
+ Returns:
37
+ Dict containing the configuration, empty dict if file doesn't exist or error occurs
38
+ """
39
+ if not config_path.exists():
40
+ return {}
41
+
42
+ try:
43
+ with open(config_path, "r", encoding="utf-8") as f:
44
+ return json.load(f)
45
+ except Exception as e:
46
+ print(f"Warning: Failed to load configuration from {config_path}: {str(e)}")
47
+ return {}
48
+
49
+ def save_config_file(config_path: Path, config_data: Dict[str, Any]) -> bool:
50
+ """
51
+ Save configuration to a file.
52
+
53
+ Args:
54
+ config_path: Path to the configuration file
55
+ config_data: Configuration data to save
56
+
57
+ Returns:
58
+ True if successful, False otherwise
59
+ """
60
+ try:
61
+ # Ensure directory exists
62
+ config_path.parent.mkdir(parents=True, exist_ok=True)
63
+
64
+ # Write configuration to file
65
+ with open(config_path, "w", encoding="utf-8") as f:
66
+ json.dump(config_data, f, indent=2)
67
+ return True
68
+ except Exception as e:
69
+ print(f"Warning: Failed to save configuration to {config_path}: {str(e)}")
70
+ return False
71
+
72
+ def merge_configs(global_config: Dict[str, Any], local_config: Dict[str, Any]) -> Dict[str, Any]:
73
+ """
74
+ Merge global and local configurations with local taking precedence.
75
+
76
+ Args:
77
+ global_config: Global configuration dictionary
78
+ local_config: Local configuration dictionary
79
+
80
+ Returns:
81
+ Merged configuration dictionary
82
+ """
83
+ # Start with global config
84
+ merged_config = global_config.copy()
85
+
86
+ # Override with local config
87
+ for key, value in local_config.items():
88
+ merged_config[key] = value
89
+
90
+ return merged_config
@@ -0,0 +1,316 @@
1
+ """
2
+ Property getters and setters for the Config class.
3
+ """
4
+ import os
5
+ import typer
6
+ from typing import Optional, Any, Union, Tuple, Tuple
7
+
8
+ class ConfigProperties:
9
+ """
10
+ Mixin class containing property getters and setters for the Config class.
11
+ This class is not meant to be instantiated directly.
12
+ """
13
+
14
+ @property
15
+ def workspace_dir(self) -> str:
16
+ """Get the current workspace directory."""
17
+ return self._workspace_dir
18
+
19
+ @workspace_dir.setter
20
+ def workspace_dir(self, path: str) -> None:
21
+ """
22
+ Set the workspace directory.
23
+
24
+ Args:
25
+ path: Path to set as workspace directory
26
+
27
+ Raises:
28
+ ValueError: If the directory doesn't exist and can't be created
29
+ """
30
+ # Convert to absolute path if not already
31
+ if not os.path.isabs(path):
32
+ path = os.path.normpath(os.path.abspath(path))
33
+ else:
34
+ # Ensure Windows paths are properly formatted
35
+ path = os.path.normpath(path)
36
+
37
+ # Check if the directory exists
38
+ if not os.path.isdir(path):
39
+ create_dir = typer.confirm(f"Workspace directory does not exist: {path}\nDo you want to create it?")
40
+ if create_dir:
41
+ try:
42
+ os.makedirs(path, exist_ok=True)
43
+ print(f"Created workspace directory: {path}")
44
+ except Exception as e:
45
+ raise ValueError(f"Failed to create workspace directory: {str(e)}") from e
46
+ else:
47
+ raise ValueError(f"Workspace directory does not exist: {path}")
48
+
49
+ self._workspace_dir = path
50
+
51
+ @property
52
+ def verbose(self) -> bool:
53
+ """Get the verbose mode status."""
54
+ return self._verbose
55
+
56
+ @verbose.setter
57
+ def verbose(self, value: bool) -> None:
58
+ """Set the verbose mode status."""
59
+ self._verbose = value
60
+ # This is a runtime setting, not persisted
61
+
62
+ # For backward compatibility
63
+ @property
64
+ def debug_mode(self) -> bool:
65
+ """Get the debug mode status (alias for verbose)."""
66
+ return self._verbose
67
+
68
+ @debug_mode.setter
69
+ def debug_mode(self, value: bool) -> None:
70
+ """Set the debug mode status (alias for verbose)."""
71
+ self._verbose = value
72
+ # This is a runtime setting, not persisted
73
+
74
+ @property
75
+ def ask_mode(self) -> bool:
76
+ """Get the ask mode status."""
77
+ return self._ask_mode
78
+
79
+ @ask_mode.setter
80
+ def ask_mode(self, value: bool) -> None:
81
+ """
82
+ Set the ask mode status.
83
+
84
+ Args:
85
+ value: Boolean value to set
86
+
87
+ Note: This setting is not persisted to config file
88
+ as it's meant to be a per-session setting.
89
+ """
90
+ # Convert tuple to boolean if needed (for backward compatibility)
91
+ if isinstance(value, tuple) and len(value) == 2:
92
+ bool_value, _ = value
93
+ self._ask_mode = bool_value
94
+ else:
95
+ self._ask_mode = value
96
+ # Don't save to config file - this is a runtime setting only
97
+
98
+ @property
99
+ def trust_mode(self) -> bool:
100
+ """Get the trust mode status."""
101
+ return self._trust_mode
102
+
103
+ @trust_mode.setter
104
+ def trust_mode(self, value: bool) -> None:
105
+ """
106
+ Set the trust mode status.
107
+
108
+ Note: This setting is not persisted to config file
109
+ as it's meant to be a per-session setting.
110
+ """
111
+ self._trust_mode = value
112
+ # Don't save to config file - this is a per-session setting
113
+
114
+ @property
115
+ def no_tools(self) -> bool:
116
+ """Get the no-tools mode status."""
117
+ return self._no_tools
118
+
119
+ @no_tools.setter
120
+ def no_tools(self, value: bool) -> None:
121
+ """
122
+ Set the no-tools mode status.
123
+
124
+ Note: This setting is not persisted to config file
125
+ as it's meant to be a per-session setting.
126
+ """
127
+ self._no_tools = value
128
+ # Don't save to config file - this is a per-session setting
129
+
130
+ @property
131
+ def temperature(self) -> float:
132
+ """Get the temperature value for model generation."""
133
+ return self._temperature
134
+
135
+ @temperature.setter
136
+ def temperature(self, value: Union[float, Tuple[float, str]]) -> None:
137
+ """
138
+ Set the temperature value for model generation.
139
+
140
+ Args:
141
+ value: Temperature value (0.0 to 1.0), or a tuple of (value, config_type)
142
+
143
+ Example:
144
+ config.temperature = 0.7 # Set runtime value only
145
+ config.temperature = (0.7, "local") # Set in local config
146
+ config.temperature = (0.7, "global") # Set in global config
147
+
148
+ Raises:
149
+ ValueError: If temperature is not between 0.0 and 1.0
150
+ """
151
+ if isinstance(value, tuple) and len(value) == 2:
152
+ temp_value, config_type = value
153
+ if temp_value < 0.0 or temp_value > 1.0:
154
+ raise ValueError("Temperature must be between 0.0 and 1.0")
155
+
156
+ self._temperature = temp_value
157
+
158
+ if config_type == "local":
159
+ self.set_local_config("temperature", temp_value)
160
+ else:
161
+ self.set_global_config("temperature", temp_value)
162
+ else:
163
+ if value < 0.0 or value > 1.0:
164
+ raise ValueError("Temperature must be between 0.0 and 1.0")
165
+
166
+ self._temperature = value
167
+ # Don't save to config file - this is a runtime setting
168
+
169
+ # top_k and top_p are now only accessible through profiles
170
+
171
+ @property
172
+ def role(self) -> str:
173
+ """Get the role for the assistant."""
174
+ return self._role
175
+
176
+ @role.setter
177
+ def role(self, value: Union[str, Tuple[str, str]]) -> None:
178
+ """
179
+ Set the role for the assistant.
180
+
181
+ Args:
182
+ value: Role string, or a tuple of (value, config_type)
183
+
184
+ Example:
185
+ config.role = "software engineer" # Set runtime value only
186
+ config.role = ("software engineer", "local") # Set in local config
187
+ config.role = ("software engineer", "global") # Set in global config
188
+ """
189
+ if isinstance(value, tuple) and len(value) == 2:
190
+ role_value, config_type = value
191
+ self._role = role_value
192
+
193
+ if config_type == "local":
194
+ self.set_local_config("role", role_value)
195
+ else:
196
+ self.set_global_config("role", role_value)
197
+ else:
198
+ self._role = value
199
+ # Don't save to config file - this is a runtime setting
200
+
201
+ @property
202
+ def gitbash_path(self) -> Optional[str]:
203
+ """Get the path to the GitBash executable."""
204
+ return self._gitbash_path
205
+
206
+ @gitbash_path.setter
207
+ def gitbash_path(self, value: Union[Optional[str], Tuple[Optional[str], str]]) -> None:
208
+ """
209
+ Set the path to the GitBash executable.
210
+
211
+ Args:
212
+ value: Path to the GitBash executable, or None to use auto-detection,
213
+ or a tuple of (value, config_type)
214
+
215
+ Example:
216
+ config.gitbash_path = "C:/Program Files/Git/bin/bash.exe" # Set runtime value only
217
+ config.gitbash_path = ("C:/Program Files/Git/bin/bash.exe", "local") # Set in local config
218
+ config.gitbash_path = ("C:/Program Files/Git/bin/bash.exe", "global") # Set in global config
219
+
220
+ Raises:
221
+ ValueError: If the provided path doesn't exist
222
+ """
223
+ if isinstance(value, tuple) and len(value) == 2:
224
+ path_value, config_type = value
225
+ # If a path is provided, verify it exists
226
+ if path_value is not None and not os.path.exists(path_value):
227
+ raise ValueError(f"GitBash executable not found at: {path_value}")
228
+
229
+ self._gitbash_path = path_value
230
+
231
+ if config_type == "local":
232
+ self.set_local_config("gitbash_path", path_value)
233
+ else:
234
+ self.set_global_config("gitbash_path", path_value)
235
+ else:
236
+ # If a path is provided, verify it exists
237
+ if value is not None and not os.path.exists(value):
238
+ raise ValueError(f"GitBash executable not found at: {value}")
239
+
240
+ self._gitbash_path = value
241
+ # Don't save to config file - this is a runtime setting
242
+
243
+ @property
244
+ def profile(self) -> Optional[str]:
245
+ """Get the current profile name."""
246
+ return self._profile
247
+
248
+ @property
249
+ def max_view_lines(self) -> int:
250
+ """Get the maximum number of lines to display before showing a warning."""
251
+ return self._merged_config.get("max_view_lines", 500)
252
+
253
+ @max_view_lines.setter
254
+ def max_view_lines(self, value: Union[int, Tuple[int, str]]) -> None:
255
+ """
256
+ Set the maximum number of lines to display before showing a warning.
257
+
258
+ Args:
259
+ value: Maximum number of lines (must be positive), or a tuple of (value, config_type)
260
+
261
+ Example:
262
+ config.max_view_lines = 1000 # Set runtime value only
263
+ config.max_view_lines = (1000, "local") # Set in local config
264
+ config.max_view_lines = (1000, "global") # Set in global config
265
+
266
+ Raises:
267
+ ValueError: If the value is not a positive integer
268
+ """
269
+ if isinstance(value, tuple) and len(value) == 2:
270
+ lines_value, config_type = value
271
+ if not isinstance(lines_value, int) or lines_value <= 0:
272
+ raise ValueError("max_view_lines must be a positive integer")
273
+
274
+ if config_type == "local":
275
+ self.set_local_config("max_view_lines", lines_value)
276
+ else:
277
+ self.set_global_config("max_view_lines", lines_value)
278
+ else:
279
+ if not isinstance(value, int) or value <= 0:
280
+ raise ValueError("max_view_lines must be a positive integer")
281
+
282
+ # This is a special case - we don't have a dedicated instance variable
283
+ # for max_view_lines, it's accessed directly from merged_config
284
+ # So we need to update the merged_config directly
285
+ self._merged_config["max_view_lines"] = value
286
+ # Don't save to config file - this is a runtime setting
287
+
288
+ @property
289
+ def show_usage_report(self) -> bool:
290
+ """Get the show usage report status."""
291
+ return self._show_usage_report
292
+
293
+ @show_usage_report.setter
294
+ def show_usage_report(self, value: Union[bool, Tuple[bool, str]]) -> None:
295
+ """
296
+ Set the show usage report status.
297
+
298
+ Args:
299
+ value: Boolean value to set, or a tuple of (value, config_type)
300
+
301
+ Example:
302
+ config.show_usage_report = True # Set runtime value only
303
+ config.show_usage_report = (True, "local") # Set in local config
304
+ config.show_usage_report = (True, "global") # Set in global config
305
+ """
306
+ if isinstance(value, tuple) and len(value) == 2:
307
+ bool_value, config_type = value
308
+ self._show_usage_report = bool_value
309
+
310
+ if config_type == "local":
311
+ self.set_local_config("show_usage_report", bool_value)
312
+ else:
313
+ self.set_global_config("show_usage_report", bool_value)
314
+ else:
315
+ self._show_usage_report = value
316
+ # Don't save to config file - this is a runtime setting