janito 0.13.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.
Files changed (50) hide show
  1. janito/__init__.py +1 -1
  2. janito/cli/agent/__init__.py +7 -0
  3. janito/cli/agent/conversation.py +149 -0
  4. janito/cli/agent/initialization.py +168 -0
  5. janito/cli/agent/query.py +112 -0
  6. janito/cli/agent.py +7 -395
  7. janito/cli/app.py +103 -19
  8. janito/cli/commands/__init__.py +12 -0
  9. janito/cli/commands/config.py +30 -0
  10. janito/cli/commands/history.py +119 -0
  11. janito/cli/commands/profile.py +93 -0
  12. janito/cli/commands/validation.py +24 -0
  13. janito/cli/commands/workspace.py +31 -0
  14. janito/cli/commands.py +9 -326
  15. janito/config/README.md +104 -0
  16. janito/config/__init__.py +16 -0
  17. janito/config/cli/__init__.py +28 -0
  18. janito/config/cli/commands.py +397 -0
  19. janito/config/cli/validators.py +77 -0
  20. janito/config/core/__init__.py +23 -0
  21. janito/config/core/file_operations.py +90 -0
  22. janito/config/core/properties.py +316 -0
  23. janito/config/core/singleton.py +282 -0
  24. janito/config/profiles/__init__.py +8 -0
  25. janito/config/profiles/definitions.py +38 -0
  26. janito/config/profiles/manager.py +80 -0
  27. janito/data/instructions_template.txt +12 -6
  28. janito/tools/__init__.py +8 -2
  29. janito/tools/bash/bash.py +80 -7
  30. janito/tools/bash/unix_persistent_bash.py +32 -1
  31. janito/tools/bash/win_persistent_bash.py +34 -1
  32. janito/tools/fetch_webpage/__init__.py +22 -33
  33. janito/tools/fetch_webpage/core.py +182 -155
  34. janito/tools/move_file.py +1 -1
  35. janito/tools/search_text.py +225 -239
  36. janito/tools/str_replace_editor/handlers/view.py +14 -8
  37. janito/tools/think.py +37 -0
  38. janito/tools/usage_tracker.py +1 -0
  39. {janito-0.13.0.dist-info → janito-0.15.0.dist-info}/METADATA +204 -23
  40. janito-0.15.0.dist-info/RECORD +64 -0
  41. janito/config.py +0 -358
  42. janito/test_file.py +0 -4
  43. janito/tools/fetch_webpage/chunking.py +0 -76
  44. janito/tools/fetch_webpage/extractors.py +0 -276
  45. janito/tools/fetch_webpage/news.py +0 -137
  46. janito/tools/fetch_webpage/utils.py +0 -108
  47. janito-0.13.0.dist-info/RECORD +0 -47
  48. {janito-0.13.0.dist-info → janito-0.15.0.dist-info}/WHEEL +0 -0
  49. {janito-0.13.0.dist-info → janito-0.15.0.dist-info}/entry_points.txt +0 -0
  50. {janito-0.13.0.dist-info → janito-0.15.0.dist-info}/licenses/LICENSE +0 -0
janito/config.py DELETED
@@ -1,358 +0,0 @@
1
- """
2
- Configuration module for Janito.
3
- Provides a singleton Config class to access configuration values.
4
- """
5
- import os
6
- import json
7
- from pathlib import Path
8
- import typer
9
- from typing import Dict, Any, Optional
10
-
11
- # Predefined parameter profiles
12
- PROFILES = {
13
- "precise": {
14
- "temperature": 0.2,
15
- "top_p": 0.85,
16
- "top_k": 20,
17
- "description": "Factual answers, documentation, structured data, avoiding hallucinations"
18
- },
19
- "balanced": {
20
- "temperature": 0.5,
21
- "top_p": 0.9,
22
- "top_k": 40,
23
- "description": "Professional writing, summarization, everyday tasks with moderate creativity"
24
- },
25
- "conversational": {
26
- "temperature": 0.7,
27
- "top_p": 0.9,
28
- "top_k": 45,
29
- "description": "Natural dialogue, educational content, support conversations"
30
- },
31
- "creative": {
32
- "temperature": 0.9,
33
- "top_p": 0.95,
34
- "top_k": 70,
35
- "description": "Storytelling, brainstorming, marketing copy, poetry"
36
- },
37
- "technical": {
38
- "temperature": 0.3,
39
- "top_p": 0.95,
40
- "top_k": 15,
41
- "description": "Code generation, debugging, decision analysis, technical problem-solving"
42
- }
43
- }
44
-
45
- class Config:
46
- """Singleton configuration class for Janito."""
47
- _instance = None
48
-
49
- def __new__(cls):
50
- if cls._instance is None:
51
- cls._instance = super(Config, cls).__new__(cls)
52
- cls._instance._workspace_dir = os.getcwd()
53
- cls._instance._verbose = False
54
- # Chat history context feature has been removed
55
- cls._instance._ask_mode = False
56
- cls._instance._trust_mode = False # New trust mode setting
57
- # Set technical profile as default
58
- profile_data = PROFILES["technical"]
59
- cls._instance._temperature = profile_data["temperature"]
60
- cls._instance._profile = "technical"
61
- cls._instance._role = "software engineer"
62
- cls._instance._gitbash_path = None # Default to None for auto-detection
63
- cls._instance._load_config()
64
- return cls._instance
65
-
66
- def _load_config(self) -> None:
67
- """Load configuration from file."""
68
- config_path = Path(self._workspace_dir) / ".janito" / "config.json"
69
- if config_path.exists():
70
- try:
71
- with open(config_path, "r", encoding="utf-8") as f:
72
- config_data = json.load(f)
73
- # Chat history context feature has been removed
74
- if "debug_mode" in config_data:
75
- self._verbose = config_data["debug_mode"]
76
- if "ask_mode" in config_data:
77
- self._ask_mode = config_data["ask_mode"]
78
- if "trust_mode" in config_data:
79
- self._trust_mode = config_data["trust_mode"]
80
- if "temperature" in config_data:
81
- self._temperature = config_data["temperature"]
82
- if "profile" in config_data:
83
- self._profile = config_data["profile"]
84
- if "role" in config_data:
85
- self._role = config_data["role"]
86
- if "gitbash_path" in config_data:
87
- self._gitbash_path = config_data["gitbash_path"]
88
- except Exception as e:
89
- print(f"Warning: Failed to load configuration: {str(e)}")
90
-
91
- def _save_config(self) -> None:
92
- """Save configuration to file."""
93
- config_dir = Path(self._workspace_dir) / ".janito"
94
- config_dir.mkdir(parents=True, exist_ok=True)
95
- config_path = config_dir / "config.json"
96
-
97
- config_data = {
98
- # Chat history context feature has been removed
99
- "verbose": self._verbose,
100
- "ask_mode": self._ask_mode,
101
- # trust_mode is not saved as it's a per-session setting
102
- "temperature": self._temperature,
103
- "role": self._role
104
- }
105
-
106
- # Save profile name if one is set
107
- if self._profile:
108
- config_data["profile"] = self._profile
109
-
110
- # Save GitBash path if one is set
111
- if self._gitbash_path:
112
- config_data["gitbash_path"] = self._gitbash_path
113
-
114
- try:
115
- with open(config_path, "w", encoding="utf-8") as f:
116
- json.dump(config_data, f, indent=2)
117
- except Exception as e:
118
- print(f"Warning: Failed to save configuration: {str(e)}")
119
-
120
- def set_profile(self, profile_name: str) -> None:
121
- """Set parameter values based on a predefined profile.
122
-
123
- Args:
124
- profile_name: Name of the profile to use (precise, balanced, conversational, creative, technical)
125
-
126
- Raises:
127
- ValueError: If the profile name is not recognized
128
- """
129
- profile_name = profile_name.lower()
130
- if profile_name not in PROFILES:
131
- valid_profiles = ", ".join(PROFILES.keys())
132
- raise ValueError(f"Unknown profile: {profile_name}. Valid profiles are: {valid_profiles}")
133
-
134
- profile = PROFILES[profile_name]
135
- self._temperature = profile["temperature"]
136
- self._profile = profile_name
137
- self._save_config()
138
-
139
- @property
140
- def profile(self) -> Optional[str]:
141
- """Get the current profile name."""
142
- return self._profile
143
-
144
- @staticmethod
145
- def get_available_profiles() -> Dict[str, Dict[str, Any]]:
146
- """Get all available predefined profiles."""
147
- return PROFILES
148
-
149
- @staticmethod
150
- def set_api_key(api_key: str) -> None:
151
- """Set the API key in the global configuration file.
152
-
153
- Args:
154
- api_key: The Anthropic API key to store
155
-
156
- Returns:
157
- None
158
- """
159
- # Create .janito directory in user's home directory if it doesn't exist
160
- home_dir = Path.home()
161
- config_dir = home_dir / ".janito"
162
- config_dir.mkdir(parents=True, exist_ok=True)
163
-
164
- # Create or update the config.json file
165
- config_path = config_dir / "config.json"
166
-
167
- # Load existing config if it exists
168
- config_data = {}
169
- if config_path.exists():
170
- try:
171
- with open(config_path, "r", encoding="utf-8") as f:
172
- config_data = json.load(f)
173
- except Exception as e:
174
- print(f"Warning: Failed to load global configuration: {str(e)}")
175
-
176
- # Update the API key
177
- config_data["api_key"] = api_key
178
-
179
- # Save the updated config
180
- try:
181
- with open(config_path, "w", encoding="utf-8") as f:
182
- json.dump(config_data, f, indent=2)
183
- print(f"API key saved to {config_path}")
184
- except Exception as e:
185
- raise ValueError(f"Failed to save API key: {str(e)}")
186
-
187
- @staticmethod
188
- def get_api_key() -> Optional[str]:
189
- """Get the API key from the global configuration file.
190
-
191
- Returns:
192
- The API key if found, None otherwise
193
- """
194
- # Look for config.json in user's home directory
195
- home_dir = Path.home()
196
- config_path = home_dir / ".janito" / "config.json"
197
-
198
- if config_path.exists():
199
- try:
200
- with open(config_path, "r", encoding="utf-8") as f:
201
- config_data = json.load(f)
202
- return config_data.get("api_key")
203
- except Exception:
204
- # Silently fail and return None
205
- pass
206
-
207
- return None
208
-
209
- @property
210
- def workspace_dir(self) -> str:
211
- """Get the current workspace directory."""
212
- return self._workspace_dir
213
-
214
- @workspace_dir.setter
215
- def workspace_dir(self, path: str) -> None:
216
- """Set the workspace directory."""
217
- # Convert to absolute path if not already
218
- if not os.path.isabs(path):
219
- path = os.path.normpath(os.path.abspath(path))
220
- else:
221
- # Ensure Windows paths are properly formatted
222
- path = os.path.normpath(path)
223
-
224
- # Check if the directory exists
225
- if not os.path.isdir(path):
226
- create_dir = typer.confirm(f"Workspace directory does not exist: {path}\nDo you want to create it?")
227
- if create_dir:
228
- try:
229
- os.makedirs(path, exist_ok=True)
230
- print(f"Created workspace directory: {path}")
231
- except Exception as e:
232
- raise ValueError(f"Failed to create workspace directory: {str(e)}") from e
233
- else:
234
- raise ValueError(f"Workspace directory does not exist: {path}")
235
-
236
- self._workspace_dir = path
237
-
238
- @property
239
- def verbose(self) -> bool:
240
- """Get the verbose mode status."""
241
- return self._verbose
242
-
243
- @verbose.setter
244
- def verbose(self, value: bool) -> None:
245
- """Set the verbose mode status."""
246
- self._verbose = value
247
-
248
- # For backward compatibility
249
- @property
250
- def debug_mode(self) -> bool:
251
- """Get the debug mode status (alias for verbose)."""
252
- return self._verbose
253
-
254
- @debug_mode.setter
255
- def debug_mode(self, value: bool) -> None:
256
- """Set the debug mode status (alias for verbose)."""
257
- self._verbose = value
258
-
259
- # Chat history context feature has been removed
260
-
261
- @property
262
- def ask_mode(self) -> bool:
263
- """Get the ask mode status."""
264
- return self._ask_mode
265
-
266
- @ask_mode.setter
267
- def ask_mode(self, value: bool) -> None:
268
- """Set the ask mode status."""
269
- self._ask_mode = value
270
- self._save_config()
271
-
272
- @property
273
- def trust_mode(self) -> bool:
274
- """Get the trust mode status."""
275
- return self._trust_mode
276
-
277
- @trust_mode.setter
278
- def trust_mode(self, value: bool) -> None:
279
- """Set the trust mode status.
280
-
281
- Note: This setting is not persisted to config file
282
- as it's meant to be a per-session setting.
283
- """
284
- self._trust_mode = value
285
- # Don't save to config file - this is a per-session setting
286
-
287
- @property
288
- def temperature(self) -> float:
289
- """Get the temperature value for model generation."""
290
- return self._temperature
291
-
292
- @temperature.setter
293
- def temperature(self, value: float) -> None:
294
- """Set the temperature value for model generation."""
295
- if value < 0.0 or value > 1.0:
296
- raise ValueError("Temperature must be between 0.0 and 1.0")
297
- self._temperature = value
298
- self._save_config()
299
-
300
- # top_k and top_p are now only accessible through profiles
301
-
302
- @property
303
- def role(self) -> str:
304
- """Get the role for the assistant."""
305
- return self._role
306
-
307
- @role.setter
308
- def role(self, value: str) -> None:
309
- """Set the role for the assistant."""
310
- self._role = value
311
- self._save_config()
312
-
313
- @property
314
- def gitbash_path(self) -> Optional[str]:
315
- """Get the path to the GitBash executable."""
316
- return self._gitbash_path
317
-
318
- @gitbash_path.setter
319
- def gitbash_path(self, value: Optional[str]) -> None:
320
- """Set the path to the GitBash executable.
321
-
322
- Args:
323
- value: Path to the GitBash executable, or None to use auto-detection
324
- """
325
- # If a path is provided, verify it exists
326
- if value is not None and not os.path.exists(value):
327
- raise ValueError(f"GitBash executable not found at: {value}")
328
-
329
- self._gitbash_path = value
330
- self._save_config()
331
-
332
- def reset_config(self) -> bool:
333
- """Reset configuration by removing the config file.
334
-
335
- Returns:
336
- bool: True if the config file was removed, False if it didn't exist
337
- """
338
- config_path = Path(self._workspace_dir) / ".janito" / "config.json"
339
- if config_path.exists():
340
- config_path.unlink()
341
- # Reset instance variables to defaults
342
- self._verbose = False
343
- # Chat history context feature has been removed
344
- self._ask_mode = False
345
- self._trust_mode = False
346
- # Set technical profile as default
347
- profile_data = PROFILES["technical"]
348
- self._temperature = profile_data["temperature"]
349
- self._profile = "technical"
350
- self._role = "software engineer"
351
- self._gitbash_path = None # Reset to auto-detection
352
- return True
353
- return False
354
-
355
- # Convenience function to get the config instance
356
- def get_config() -> Config:
357
- """Get the singleton Config instance."""
358
- return Config()
janito/test_file.py DELETED
@@ -1,4 +0,0 @@
1
- def add(a, b):
2
- return a - b # This has a bug, it should be a + b
3
-
4
- print(add(5, 3)) # Should print 8, but will print 2
@@ -1,76 +0,0 @@
1
- """
2
- Functions for chunking large content into manageable pieces.
3
- """
4
-
5
- from typing import List
6
- from janito.tools.rich_console import print_info, print_success
7
-
8
-
9
- def chunk_large_content(text: str, chunk_size: int = 4000, overlap: int = 500) -> List[str]:
10
- """
11
- Split very large text content into manageable chunks suitable for LLM processing.
12
-
13
- Args:
14
- text: The text to chunk
15
- chunk_size: Target size for each chunk in characters
16
- overlap: Number of characters to overlap between chunks
17
-
18
- Returns:
19
- List of text chunks
20
- """
21
- if not text or len(text) <= chunk_size:
22
- return [text] if text else []
23
-
24
- print_info(f"Chunking {len(text)} characters of text into ~{chunk_size} character chunks", "Content Chunking")
25
-
26
- # Try to split on paragraph breaks first
27
- paragraphs = text.split('\n\n')
28
- chunks = []
29
- current_chunk = ""
30
-
31
- for para in paragraphs:
32
- # If adding this paragraph would exceed chunk size
33
- if len(current_chunk) + len(para) + 2 > chunk_size:
34
- # If current chunk is not empty, add it to chunks
35
- if current_chunk:
36
- chunks.append(current_chunk)
37
- # Start new chunk with overlap from previous chunk
38
- if overlap > 0 and len(current_chunk) > overlap:
39
- current_chunk = current_chunk[-overlap:] + "\n\n" + para
40
- else:
41
- current_chunk = para
42
- else:
43
- # If paragraph itself is bigger than chunk size, split it
44
- if len(para) > chunk_size:
45
- words = para.split()
46
- temp_chunk = ""
47
- for word in words:
48
- if len(temp_chunk) + len(word) + 1 > chunk_size:
49
- chunks.append(temp_chunk)
50
- # Start new chunk with overlap
51
- if overlap > 0 and len(temp_chunk) > overlap:
52
- temp_chunk = temp_chunk[-overlap:] + " " + word
53
- else:
54
- temp_chunk = word
55
- else:
56
- if temp_chunk:
57
- temp_chunk += " " + word
58
- else:
59
- temp_chunk = word
60
- if temp_chunk:
61
- current_chunk = temp_chunk
62
- else:
63
- chunks.append(para)
64
- else:
65
- # Add paragraph to current chunk
66
- if current_chunk:
67
- current_chunk += "\n\n" + para
68
- else:
69
- current_chunk = para
70
-
71
- # Don't forget the last chunk
72
- if current_chunk:
73
- chunks.append(current_chunk)
74
-
75
- print_success(f"Text chunked into {len(chunks)} segments", "Content Chunking")
76
- return chunks