janito 0.11.0__py3-none-any.whl → 0.12.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/__main__.py +6 -204
  3. janito/callbacks.py +34 -132
  4. janito/cli/__init__.py +6 -0
  5. janito/cli/agent.py +287 -0
  6. janito/cli/app.py +86 -0
  7. janito/cli/commands.py +329 -0
  8. janito/cli/output.py +29 -0
  9. janito/cli/utils.py +22 -0
  10. janito/config.py +338 -121
  11. janito/data/instructions_template.txt +27 -0
  12. janito/token_report.py +154 -145
  13. janito/tools/__init__.py +38 -21
  14. janito/tools/bash/bash.py +82 -0
  15. janito/tools/bash/unix_persistent_bash.py +182 -0
  16. janito/tools/bash/win_persistent_bash.py +306 -0
  17. janito/tools/decorators.py +2 -13
  18. janito/tools/delete_file.py +27 -9
  19. janito/tools/fetch_webpage/__init__.py +34 -0
  20. janito/tools/fetch_webpage/chunking.py +76 -0
  21. janito/tools/fetch_webpage/core.py +155 -0
  22. janito/tools/fetch_webpage/extractors.py +276 -0
  23. janito/tools/fetch_webpage/news.py +137 -0
  24. janito/tools/fetch_webpage/utils.py +108 -0
  25. janito/tools/find_files.py +106 -44
  26. janito/tools/move_file.py +72 -0
  27. janito/tools/prompt_user.py +37 -6
  28. janito/tools/replace_file.py +31 -4
  29. janito/tools/rich_console.py +139 -0
  30. janito/tools/search_text.py +33 -21
  31. janito/tools/str_replace_editor/editor.py +7 -4
  32. janito/tools/str_replace_editor/handlers/__init__.py +16 -0
  33. janito/tools/str_replace_editor/handlers/create.py +60 -0
  34. janito/tools/str_replace_editor/handlers/insert.py +100 -0
  35. janito/tools/str_replace_editor/handlers/str_replace.py +92 -0
  36. janito/tools/str_replace_editor/handlers/undo.py +64 -0
  37. janito/tools/str_replace_editor/handlers/view.py +153 -0
  38. janito/tools/str_replace_editor/utils.py +0 -1
  39. janito/tools/usage_tracker.py +136 -0
  40. janito-0.12.0.dist-info/METADATA +203 -0
  41. janito-0.12.0.dist-info/RECORD +47 -0
  42. janito/chat_history.py +0 -117
  43. janito/data/instructions.txt +0 -4
  44. janito/tools/bash.py +0 -22
  45. janito/tools/str_replace_editor/handlers.py +0 -335
  46. janito-0.11.0.dist-info/METADATA +0 -86
  47. janito-0.11.0.dist-info/RECORD +0 -26
  48. {janito-0.11.0.dist-info → janito-0.12.0.dist-info}/WHEEL +0 -0
  49. {janito-0.11.0.dist-info → janito-0.12.0.dist-info}/entry_points.txt +0 -0
  50. {janito-0.11.0.dist-info → janito-0.12.0.dist-info}/licenses/LICENSE +0 -0
janito/cli/app.py ADDED
@@ -0,0 +1,86 @@
1
+ """
2
+ Main CLI application for Janito.
3
+ """
4
+ import sys
5
+ from typing import Optional
6
+ import typer
7
+ from rich.console import Console
8
+ import importlib.metadata
9
+
10
+ from janito.config import get_config
11
+ from janito.cli.commands import handle_config_commands, validate_parameters
12
+ from janito.cli.agent import handle_query
13
+ from janito.cli.utils import get_stdin_termination_hint
14
+
15
+ app = typer.Typer()
16
+ console = Console()
17
+
18
+ @app.callback(invoke_without_command=True)
19
+ def main(ctx: typer.Context,
20
+ query: Optional[str] = typer.Argument(None, help="Query to send to the claudine agent"),
21
+ verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose mode with detailed output"),
22
+ show_tokens: bool = typer.Option(False, "--show-tokens", "-t", help="Show detailed token usage and pricing information"),
23
+ workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Set the workspace directory"),
24
+ config_str: Optional[str] = typer.Option(None, "--set-config", help="Configuration string in format 'key=value', e.g., 'temperature=0.7' or 'profile=technical'"),
25
+ show_config: bool = typer.Option(False, "--show-config", help="Show current configuration"),
26
+ reset_config: bool = typer.Option(False, "--reset-config", help="Reset configuration by removing the config file"),
27
+ set_api_key: Optional[str] = typer.Option(None, "--set-api-key", help="Set the Anthropic API key globally in the user's home directory"),
28
+ ask: bool = typer.Option(False, "--ask", help="Enable ask mode which disables tools that perform changes"),
29
+ temperature: float = typer.Option(0.0, "--temperature", help="Set the temperature for model generation (0.0 to 1.0)"),
30
+ profile: Optional[str] = typer.Option(None, "--profile", help="Use a predefined parameter profile (precise, balanced, conversational, creative, technical)"),
31
+ role: Optional[str] = typer.Option(None, "--role", help="Set the assistant's role (default: 'software engineer')"),
32
+ version: bool = typer.Option(False, "--version", help="Show the version and exit"),
33
+ continue_conversation: bool = typer.Option(False, "--continue", "-c", help="Continue the previous conversation")):
34
+ """
35
+ Janito CLI tool. If a query is provided without a command, it will be sent to the claudine agent.
36
+ """
37
+ # Set verbose mode in config
38
+ get_config().verbose = verbose
39
+
40
+ # Set ask mode in config
41
+ get_config().ask_mode = ask
42
+
43
+ # Show a message if ask mode is enabled
44
+ if ask:
45
+ console.print("[bold yellow]⚠️ Ask Mode enabled:[/bold yellow] 🔒 Tools that perform changes are disabled")
46
+
47
+ # Show version and exit if requested
48
+ if version:
49
+ try:
50
+ version_str = importlib.metadata.version("janito")
51
+ console.print(f"🚀 Janito version: {version_str}")
52
+ except importlib.metadata.PackageNotFoundError:
53
+ console.print("🚀 Janito version: [italic]development[/italic]")
54
+ sys.exit(0)
55
+
56
+ # Validate temperature
57
+ validate_parameters(temperature)
58
+
59
+ # Handle configuration-related commands
60
+ exit_after_config = handle_config_commands(
61
+ ctx,
62
+ reset_config,
63
+ workspace,
64
+ show_config,
65
+ profile,
66
+ role,
67
+ set_api_key,
68
+ config_str,
69
+ query,
70
+ continue_conversation
71
+ )
72
+
73
+ if exit_after_config:
74
+ sys.exit(0)
75
+
76
+ # Handle query if no subcommand was invoked
77
+ if ctx.invoked_subcommand is None:
78
+ # If no query provided in command line, read from stdin
79
+ if not query:
80
+ console.print("[bold blue]📝 No query provided in command line. Reading from stdin...[/bold blue]")
81
+ console.print(get_stdin_termination_hint())
82
+ query = sys.stdin.read().strip()
83
+
84
+ # Only proceed if we have a query (either from command line or stdin)
85
+ if query:
86
+ handle_query(query, temperature, verbose, show_tokens, continue_conversation)
janito/cli/commands.py ADDED
@@ -0,0 +1,329 @@
1
+ """
2
+ Command handling logic for Janito CLI.
3
+ """
4
+ import sys
5
+ import os
6
+ from typing import Optional, Tuple, Any
7
+ from pathlib import Path
8
+ import typer
9
+ from rich.console import Console
10
+
11
+ from janito.config import get_config, Config
12
+
13
+ console = Console()
14
+
15
+ def validate_parameters(temperature: float) -> None:
16
+ """
17
+ Validate temperature parameter.
18
+
19
+ Args:
20
+ temperature: Temperature value for model generation
21
+ """
22
+ try:
23
+ if temperature < 0.0 or temperature > 1.0:
24
+ raise ValueError("Temperature must be between 0.0 and 1.0")
25
+
26
+ # We'll use this value directly in the agent initialization but we don't save it to config
27
+ # Temperature display is hidden
28
+ except ValueError as e:
29
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
30
+ sys.exit(1)
31
+
32
+ def handle_reset_config(reset_config: bool, ctx: typer.Context, query: Optional[str]) -> bool:
33
+ """
34
+ Handle the --reset-config parameter.
35
+
36
+ Args:
37
+ reset_config: Whether to reset the configuration
38
+ ctx: Typer context
39
+ query: Query string
40
+
41
+ Returns:
42
+ bool: True if the program should exit after this operation
43
+ """
44
+ if reset_config:
45
+ try:
46
+ config_path = Path(get_config().workspace_dir) / ".janito" / "config.json"
47
+ if get_config().reset_config():
48
+ console.print(f"[bold green]✅ Configuration file removed: {config_path}[/bold green]")
49
+ else:
50
+ console.print(f"[bold yellow]⚠️ Configuration file does not exist: {config_path}[/bold yellow]")
51
+ except Exception as e:
52
+ console.print(f"[bold red]Error removing configuration file:[/bold red] {str(e)}")
53
+
54
+ # Exit after resetting config if no other operation is requested
55
+ return ctx.invoked_subcommand is None and not query
56
+
57
+ return False
58
+
59
+ def handle_workspace(workspace: Optional[str]) -> bool:
60
+ """
61
+ Handle the --workspace parameter.
62
+
63
+ Args:
64
+ workspace: Workspace directory path
65
+
66
+ Returns:
67
+ bool: True if the program should exit after this operation
68
+ """
69
+ if workspace:
70
+ try:
71
+ console.print(f"[bold]📂 Setting workspace directory to: {workspace}[/bold]")
72
+ get_config().workspace_dir = workspace
73
+ console.print(f"[bold green]✅ Workspace directory set to: {get_config().workspace_dir}[/bold green]")
74
+ except ValueError as e:
75
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
76
+ sys.exit(1)
77
+
78
+ return False
79
+
80
+ def handle_show_config(show_config: bool, ctx: typer.Context, query: Optional[str]) -> bool:
81
+ """
82
+ Handle the --show-config parameter.
83
+
84
+ Args:
85
+ show_config: Whether to show the configuration
86
+ ctx: Typer context
87
+ query: Query string
88
+
89
+ Returns:
90
+ bool: True if the program should exit after this operation
91
+ """
92
+ if show_config:
93
+ config = get_config()
94
+ console.print("[bold blue]⚙️ Current Configuration:[/bold blue]")
95
+ console.print(f"[bold]📁 Local Configuration File:[/bold] .janito/config.json")
96
+ console.print(f"[bold]🏠 Global Configuration File:[/bold] {Path.home() / '.janito' / 'config.json'}")
97
+
98
+ # Show API key status
99
+ api_key_global = Config.get_api_key()
100
+ api_key_env = os.environ.get("ANTHROPIC_API_KEY")
101
+ if api_key_global:
102
+ console.print(f"[bold]🔑 API Key:[/bold] [green]Set in global config[/green]")
103
+ elif api_key_env:
104
+ console.print(f"[bold]🔑 API Key:[/bold] [yellow]Set in environment variable[/yellow]")
105
+ else:
106
+ console.print(f"[bold]🔑 API Key:[/bold] [red]Not set[/red]")
107
+
108
+ console.print(f"[bold]🔊 Verbose Mode:[/bold] {'Enabled' if config.verbose else 'Disabled'}")
109
+ console.print(f"[bold]❓ Ask Mode:[/bold] {'Enabled' if config.ask_mode else 'Disabled'}")
110
+
111
+ console.print(f"[bold]👤 Role:[/bold] {config.role}")
112
+
113
+ # Show profile information if one is set
114
+ if config.profile:
115
+ profile_data = config.get_available_profiles()[config.profile]
116
+ console.print(f"[bold]📋 Active Profile:[/bold] {config.profile} - {profile_data['description']}")
117
+
118
+ # Show available profiles
119
+ profiles = config.get_available_profiles()
120
+ if profiles:
121
+ console.print("\n[bold blue]📋 Available Parameter Profiles:[/bold blue]")
122
+ for name, data in profiles.items():
123
+ console.print(f"[bold]🔹 {name}[/bold] - {data['description']}")
124
+
125
+ # Exit if this was the only operation requested
126
+ return ctx.invoked_subcommand is None and not query
127
+
128
+ return False
129
+
130
+ def handle_profile(profile: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
131
+ """
132
+ Handle the --profile parameter.
133
+
134
+ Args:
135
+ profile: Profile name
136
+ ctx: Typer context
137
+ query: Query string
138
+
139
+ Returns:
140
+ bool: True if the program should exit after this operation
141
+ """
142
+ if profile is not None:
143
+ try:
144
+ # Apply profile without saving to config
145
+ config = get_config()
146
+ profile_data = config.get_available_profiles()[profile.lower()]
147
+
148
+ # Set values directly without saving
149
+ config._temperature = profile_data["temperature"]
150
+ config._profile = profile.lower()
151
+
152
+ console.print(f"[bold green]✅ Profile '{profile.lower()}' applied for this session only[/bold green]")
153
+ console.print(f"[dim]📝 Description: {profile_data['description']}[/dim]")
154
+
155
+ # Exit after applying profile if no other operation is requested
156
+ return ctx.invoked_subcommand is None and not query
157
+ except ValueError as e:
158
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
159
+ sys.exit(1)
160
+
161
+ return False
162
+
163
+ def handle_role(role: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
164
+ """
165
+ Handle the --role parameter.
166
+
167
+ Args:
168
+ role: Role name
169
+ ctx: Typer context
170
+ query: Query string
171
+
172
+ Returns:
173
+ bool: True if the program should exit after this operation
174
+ """
175
+ if role is not None:
176
+ try:
177
+ # Set role directly without saving to config
178
+ config = get_config()
179
+ config._role = role
180
+
181
+ console.print(f"[bold green]✅ Role '{role}' applied for this session only[/bold green]")
182
+
183
+ # Exit after applying role if no other operation is requested
184
+ return ctx.invoked_subcommand is None and not query
185
+ except Exception as e:
186
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
187
+ sys.exit(1)
188
+
189
+ return False
190
+
191
+ def handle_set_api_key(set_api_key: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
192
+ """
193
+ Handle the --set-api-key parameter.
194
+
195
+ Args:
196
+ set_api_key: API key
197
+ ctx: Typer context
198
+ query: Query string
199
+
200
+ Returns:
201
+ bool: True if the program should exit after this operation
202
+ """
203
+ if set_api_key is not None:
204
+ try:
205
+ Config.set_api_key(set_api_key)
206
+ console.print(f"[bold green]✅ API key saved to global configuration[/bold green]")
207
+ console.print(f"[dim]📁 Location: {Path.home() / '.janito' / 'config.json'}[/dim]")
208
+
209
+ # Exit after setting API key if no other operation is requested
210
+ return ctx.invoked_subcommand is None and not query
211
+ except Exception as e:
212
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
213
+ sys.exit(1)
214
+
215
+ return False
216
+
217
+ def handle_set_config(config_str: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
218
+ """
219
+ Handle the --set-config parameter.
220
+
221
+ Args:
222
+ config_str: Configuration string in format 'key=value'
223
+ ctx: Typer context
224
+ query: Query string
225
+
226
+ Returns:
227
+ bool: True if the program should exit after this operation
228
+ """
229
+ if config_str is not None:
230
+ try:
231
+ # Parse the config string
232
+ config_parts = config_str.split("=", 1)
233
+ if len(config_parts) != 2:
234
+ console.print(f"[bold red]Error:[/bold red] Invalid configuration format. Use 'key=value' format.")
235
+ return ctx.invoked_subcommand is None and not query
236
+
237
+ key = config_parts[0].strip()
238
+ value = config_parts[1].strip()
239
+
240
+ # Remove quotes if present
241
+ if (value.startswith("'") and value.endswith("'")) or \
242
+ (value.startswith('"') and value.endswith('"')):
243
+ value = value[1:-1]
244
+
245
+ if key == "profile":
246
+ try:
247
+ get_config().set_profile(value)
248
+ profile_data = get_config().get_available_profiles()[value.lower()]
249
+ console.print(f"[bold green]✅ Profile set to '{value.lower()}'[/bold green]")
250
+ console.print(f"[dim]📝 Description: {profile_data['description']}[/dim]")
251
+ except ValueError as e:
252
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
253
+ elif key == "temperature":
254
+ try:
255
+ temp_value = float(value)
256
+ if temp_value < 0.0 or temp_value > 1.0:
257
+ console.print("[bold red]Error:[/bold red] Temperature must be between 0.0 and 1.0")
258
+ return ctx.invoked_subcommand is None and not query
259
+
260
+ get_config().temperature = temp_value
261
+ console.print(f"[bold green]✅ Temperature set to {temp_value} and saved to configuration[/bold green]")
262
+ except ValueError:
263
+ console.print(f"[bold red]Error:[/bold red] Invalid temperature value: {value}. Must be a float between 0.0 and 1.0.")
264
+ # top_k and top_p are now only accessible through profiles
265
+ elif key == "role":
266
+ get_config().role = value
267
+ console.print(f"[bold green]✅ Role set to '{value}' and saved to configuration[/bold green]")
268
+ else:
269
+ console.print(f"[bold yellow]Warning:[/bold yellow] Unsupported configuration key: {key}")
270
+
271
+ # Exit after applying config changes if no other operation is requested
272
+ return ctx.invoked_subcommand is None and not query
273
+ except Exception as e:
274
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
275
+
276
+ return False
277
+
278
+ def handle_config_commands(
279
+ ctx: typer.Context,
280
+ reset_config: bool,
281
+ workspace: Optional[str],
282
+ show_config: bool,
283
+ profile: Optional[str],
284
+ role: Optional[str],
285
+ set_api_key: Optional[str],
286
+ config_str: Optional[str],
287
+ query: Optional[str],
288
+ continue_conversation: bool = False
289
+ ) -> bool:
290
+ """
291
+ Handle all configuration-related commands.
292
+
293
+ Args:
294
+ ctx: Typer context
295
+ reset_config: Whether to reset the configuration
296
+ workspace: Workspace directory path
297
+ show_config: Whether to show the configuration
298
+ profile: Profile name
299
+ role: Role name
300
+ set_api_key: API key
301
+ config_str: Configuration string in format 'key=value'
302
+ query: Query string
303
+ continue_conversation: Whether to continue the previous conversation
304
+
305
+ Returns:
306
+ bool: True if the program should exit after these operations
307
+ """
308
+ # Handle each command and check if we should exit after it
309
+ if handle_reset_config(reset_config, ctx, query):
310
+ return True
311
+
312
+ handle_workspace(workspace)
313
+
314
+ if handle_show_config(show_config, ctx, query):
315
+ return True
316
+
317
+ if handle_profile(profile, ctx, query):
318
+ return True
319
+
320
+ if handle_role(role, ctx, query):
321
+ return True
322
+
323
+ if handle_set_api_key(set_api_key, ctx, query):
324
+ return True
325
+
326
+ if handle_set_config(config_str, ctx, query):
327
+ return True
328
+
329
+ return False
janito/cli/output.py ADDED
@@ -0,0 +1,29 @@
1
+ """
2
+ Output formatting and display for Janito CLI.
3
+ """
4
+ from rich.console import Console
5
+ from janito.config import get_config
6
+
7
+ console = Console()
8
+
9
+ def display_generation_params(
10
+ temp_to_use: float,
11
+ profile_data: dict = None,
12
+ temperature: float = 0.0
13
+ ) -> None:
14
+ """
15
+ Display generation parameters in verbose mode.
16
+
17
+ Args:
18
+ temp_to_use: The temperature value being used
19
+ profile_data: The profile data if a profile is being used
20
+ temperature: The temperature value from command line
21
+ """
22
+ # Show profile information if one is active
23
+ config = get_config()
24
+ if config.profile:
25
+ if not profile_data:
26
+ profile_data = config.get_available_profiles()[config.profile]
27
+ console.print(f"[dim]👤 Using profile: {config.profile} - {profile_data['description']}[/dim]")
28
+
29
+ # Temperature, top_k, and top_p information is hidden
janito/cli/utils.py ADDED
@@ -0,0 +1,22 @@
1
+ """
2
+ Utility functions for the CLI module.
3
+ """
4
+ import platform
5
+ from rich.console import Console
6
+
7
+ console = Console()
8
+
9
+ def get_stdin_termination_hint():
10
+ """
11
+ Returns a user-friendly message about how to terminate stdin input
12
+ based on the current platform.
13
+
14
+ Returns:
15
+ str: A message with the key sequence to terminate stdin input
16
+ """
17
+ system = platform.system()
18
+
19
+ if system == "Windows":
20
+ return "[bold yellow]Press Ctrl+Z followed by Enter to terminate input[/bold yellow]"
21
+ else: # Unix-like systems (Linux, macOS)
22
+ return "[bold yellow]Press Ctrl+D to terminate input[/bold yellow]"