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.
- janito/__init__.py +1 -1
- janito/cli/agent/initialization.py +4 -8
- janito/cli/agent/query.py +29 -25
- janito/cli/app.py +17 -21
- janito/cli/commands/config.py +25 -237
- janito/cli/commands/profile.py +92 -71
- janito/cli/commands/workspace.py +30 -30
- janito/config/README.md +104 -0
- janito/config/__init__.py +16 -0
- janito/config/cli/__init__.py +28 -0
- janito/config/cli/commands.py +397 -0
- janito/config/cli/validators.py +77 -0
- janito/config/core/__init__.py +23 -0
- janito/config/core/file_operations.py +90 -0
- janito/config/core/properties.py +316 -0
- janito/config/core/singleton.py +282 -0
- janito/config/profiles/__init__.py +8 -0
- janito/config/profiles/definitions.py +38 -0
- janito/config/profiles/manager.py +80 -0
- janito/data/instructions_template.txt +6 -3
- janito/tools/bash/bash.py +80 -7
- janito/tools/bash/unix_persistent_bash.py +32 -1
- janito/tools/bash/win_persistent_bash.py +34 -1
- janito/tools/move_file.py +1 -1
- janito/tools/str_replace_editor/handlers/view.py +14 -8
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/METADATA +107 -22
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/RECORD +31 -20
- janito/config.py +0 -375
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/WHEEL +0 -0
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/entry_points.txt +0 -0
- {janito-0.14.0.dist-info → janito-0.15.0.dist-info}/licenses/LICENSE +0 -0
@@ -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
|