janito 0.12.0__py3-none-any.whl → 0.14.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 (40) 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 +172 -0
  5. janito/cli/agent/query.py +108 -0
  6. janito/cli/agent.py +7 -282
  7. janito/cli/app.py +105 -9
  8. janito/cli/commands/__init__.py +12 -0
  9. janito/cli/commands/config.py +242 -0
  10. janito/cli/commands/history.py +119 -0
  11. janito/cli/commands/profile.py +72 -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.py +37 -0
  16. janito/data/instructions_template.txt +9 -5
  17. janito/tools/__init__.py +8 -2
  18. janito/tools/bash/bash.py +3 -1
  19. janito/tools/bash/unix_persistent_bash.py +183 -181
  20. janito/tools/bash/win_persistent_bash.py +4 -2
  21. janito/tools/fetch_webpage/__init__.py +22 -33
  22. janito/tools/fetch_webpage/core.py +182 -155
  23. janito/tools/rich_console.py +46 -9
  24. janito/tools/search_text.py +225 -238
  25. janito/tools/str_replace_editor/handlers/str_replace.py +3 -1
  26. janito/tools/str_replace_editor/handlers/view.py +14 -8
  27. janito/tools/think.py +37 -0
  28. janito/tools/usage_tracker.py +1 -0
  29. janito-0.14.0.dist-info/METADATA +396 -0
  30. janito-0.14.0.dist-info/RECORD +53 -0
  31. janito/test_file.py +0 -4
  32. janito/tools/fetch_webpage/chunking.py +0 -76
  33. janito/tools/fetch_webpage/extractors.py +0 -276
  34. janito/tools/fetch_webpage/news.py +0 -137
  35. janito/tools/fetch_webpage/utils.py +0 -108
  36. janito-0.12.0.dist-info/METADATA +0 -203
  37. janito-0.12.0.dist-info/RECORD +0 -47
  38. {janito-0.12.0.dist-info → janito-0.14.0.dist-info}/WHEEL +0 -0
  39. {janito-0.12.0.dist-info → janito-0.14.0.dist-info}/entry_points.txt +0 -0
  40. {janito-0.12.0.dist-info → janito-0.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,242 @@
1
+ """
2
+ Configuration management functions for Janito CLI.
3
+ """
4
+ import sys
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Optional
8
+ import typer
9
+ from rich.console import Console
10
+
11
+ from janito.config import get_config, Config
12
+ from janito.cli.commands.workspace import handle_workspace
13
+ from janito.cli.commands.profile import handle_profile, handle_role
14
+ from janito.cli.commands.history import handle_history
15
+
16
+ console = Console()
17
+
18
+ def handle_reset_config(reset_config: bool, ctx: typer.Context, query: Optional[str]) -> bool:
19
+ """
20
+ Handle the --reset-config parameter.
21
+
22
+ Args:
23
+ reset_config: Whether to reset the configuration
24
+ ctx: Typer context
25
+ query: Query string
26
+
27
+ Returns:
28
+ bool: True if the program should exit after this operation
29
+ """
30
+ if reset_config:
31
+ try:
32
+ config_path = Path(get_config().workspace_dir) / ".janito" / "config.json"
33
+ if get_config().reset_config():
34
+ console.print(f"[bold green]✅ Configuration file removed: {config_path}[/bold green]")
35
+ else:
36
+ console.print(f"[bold yellow]⚠️ Configuration file does not exist: {config_path}[/bold yellow]")
37
+ except Exception as e:
38
+ console.print(f"[bold red]Error removing configuration file:[/bold red] {str(e)}")
39
+
40
+ # Exit after resetting config if no other operation is requested
41
+ return ctx.invoked_subcommand is None and not query
42
+
43
+ return False
44
+
45
+ def handle_show_config(show_config: bool, ctx: typer.Context, query: Optional[str]) -> bool:
46
+ """
47
+ Handle the --show-config parameter.
48
+
49
+ Args:
50
+ show_config: Whether to show the configuration
51
+ ctx: Typer context
52
+ query: Query string
53
+
54
+ Returns:
55
+ bool: True if the program should exit after this operation
56
+ """
57
+ if show_config:
58
+ config = get_config()
59
+ console.print("[bold blue]⚙️ Current Configuration:[/bold blue]")
60
+ console.print(f"[bold]📁 Local Configuration File:[/bold] .janito/config.json")
61
+ console.print(f"[bold]🏠 Global Configuration File:[/bold] {Path.home() / '.janito' / 'config.json'}")
62
+
63
+ # Show API key status
64
+ api_key_global = Config.get_api_key()
65
+ api_key_env = os.environ.get("ANTHROPIC_API_KEY")
66
+ if api_key_global:
67
+ console.print(f"[bold]🔑 API Key:[/bold] [green]Set in global config[/green]")
68
+ elif api_key_env:
69
+ console.print(f"[bold]🔑 API Key:[/bold] [yellow]Set in environment variable[/yellow]")
70
+ else:
71
+ console.print(f"[bold]🔑 API Key:[/bold] [red]Not set[/red]")
72
+
73
+ console.print(f"[bold]🔊 Verbose Mode:[/bold] {'Enabled' if config.verbose else 'Disabled'}")
74
+ console.print(f"[bold]❓ Ask Mode:[/bold] {'Enabled' if config.ask_mode else 'Disabled'}")
75
+
76
+ console.print(f"[bold]👤 Role:[/bold] {config.role}")
77
+
78
+ # Show profile information if one is set
79
+ if config.profile:
80
+ profile_data = config.get_available_profiles()[config.profile]
81
+ console.print(f"[bold]📋 Active Profile:[/bold] {config.profile} - {profile_data['description']}")
82
+
83
+ # Show available profiles
84
+ profiles = config.get_available_profiles()
85
+ if profiles:
86
+ console.print("\n[bold blue]📋 Available Parameter Profiles:[/bold blue]")
87
+ for name, data in profiles.items():
88
+ console.print(f"[bold]🔹 {name}[/bold] - {data['description']}")
89
+
90
+ # Exit if this was the only operation requested
91
+ return ctx.invoked_subcommand is None and not query
92
+
93
+ return False
94
+
95
+ def handle_set_api_key(set_api_key: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
96
+ """
97
+ Handle the --set-api-key parameter.
98
+
99
+ Args:
100
+ set_api_key: API key
101
+ ctx: Typer context
102
+ query: Query string
103
+
104
+ Returns:
105
+ bool: True if the program should exit after this operation
106
+ """
107
+ if set_api_key is not None:
108
+ try:
109
+ Config.set_api_key(set_api_key)
110
+ console.print(f"[bold green]✅ API key saved to global configuration[/bold green]")
111
+ console.print(f"[dim]📁 Location: {Path.home() / '.janito' / 'config.json'}[/dim]")
112
+
113
+ # Exit after setting API key if no other operation is requested
114
+ return ctx.invoked_subcommand is None and not query
115
+ except Exception as e:
116
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
117
+ sys.exit(1)
118
+
119
+ return False
120
+
121
+ def handle_set_config(config_str: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
122
+ """
123
+ Handle the --set-config parameter.
124
+
125
+ Args:
126
+ config_str: Configuration string in format 'key=value'
127
+ ctx: Typer context
128
+ query: Query string
129
+
130
+ Returns:
131
+ bool: True if the program should exit after this operation
132
+ """
133
+ if config_str is not None:
134
+ try:
135
+ # Parse the config string
136
+ config_parts = config_str.split("=", 1)
137
+ if len(config_parts) != 2:
138
+ console.print(f"[bold red]Error:[/bold red] Invalid configuration format. Use 'key=value' format.")
139
+ return ctx.invoked_subcommand is None and not query
140
+
141
+ key = config_parts[0].strip()
142
+ value = config_parts[1].strip()
143
+
144
+ # Remove quotes if present
145
+ if (value.startswith("'") and value.endswith("'")) or \
146
+ (value.startswith('"') and value.endswith('"')):
147
+ value = value[1:-1]
148
+
149
+ if key == "profile":
150
+ try:
151
+ get_config().set_profile(value)
152
+ profile_data = get_config().get_available_profiles()[value.lower()]
153
+ console.print(f"[bold green]✅ Profile set to '{value.lower()}'[/bold green]")
154
+ console.print(f"[dim]📝 Description: {profile_data['description']}[/dim]")
155
+ except ValueError as e:
156
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
157
+ elif key == "temperature":
158
+ try:
159
+ temp_value = float(value)
160
+ if temp_value < 0.0 or temp_value > 1.0:
161
+ console.print("[bold red]Error:[/bold red] Temperature must be between 0.0 and 1.0")
162
+ return ctx.invoked_subcommand is None and not query
163
+
164
+ get_config().temperature = temp_value
165
+ console.print(f"[bold green]✅ Temperature set to {temp_value} and saved to configuration[/bold green]")
166
+ except ValueError:
167
+ console.print(f"[bold red]Error:[/bold red] Invalid temperature value: {value}. Must be a float between 0.0 and 1.0.")
168
+ # top_k and top_p are now only accessible through profiles
169
+ elif key == "role":
170
+ get_config().role = value
171
+ console.print(f"[bold green]✅ Role set to '{value}' and saved to configuration[/bold green]")
172
+ else:
173
+ console.print(f"[bold yellow]Warning:[/bold yellow] Unsupported configuration key: {key}")
174
+
175
+ # Exit after applying config changes if no other operation is requested
176
+ return ctx.invoked_subcommand is None and not query
177
+ except Exception as e:
178
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
179
+
180
+ return False
181
+
182
+ def handle_config_commands(
183
+ ctx: typer.Context,
184
+ reset_config: bool,
185
+ workspace: Optional[str],
186
+ show_config: bool,
187
+ profile: Optional[str],
188
+ role: Optional[str],
189
+ set_api_key: Optional[str],
190
+ config_str: Optional[str],
191
+ query: Optional[str],
192
+ continue_id: Optional[str] = None,
193
+ continue_flag: Optional[str] = None,
194
+ history_flag: bool = False,
195
+ history_count: Optional[int] = None
196
+ ) -> bool:
197
+ """
198
+ Handle all configuration-related commands.
199
+
200
+ Args:
201
+ ctx: Typer context
202
+ reset_config: Whether to reset the configuration
203
+ workspace: Workspace directory path
204
+ show_config: Whether to show the configuration
205
+ profile: Profile name
206
+ role: Role name
207
+ set_api_key: API key
208
+ config_str: Configuration string in format 'key=value'
209
+ query: Query string
210
+ continue_id: Optional message ID to continue a specific conversation
211
+ continue_flag: Optional string that can be empty (flag only) or contain a chat ID
212
+ history_flag: Whether to show conversation history (--history flag)
213
+ history_count: Number of history entries to display (value after --history)
214
+
215
+ Returns:
216
+ bool: True if the program should exit after these operations
217
+ """
218
+ # Handle each command and check if we should exit after it
219
+ if handle_reset_config(reset_config, ctx, query):
220
+ return True
221
+
222
+ handle_workspace(workspace)
223
+
224
+ if handle_show_config(show_config, ctx, query):
225
+ return True
226
+
227
+ if handle_profile(profile, ctx, query):
228
+ return True
229
+
230
+ if handle_role(role, ctx, query):
231
+ return True
232
+
233
+ if handle_set_api_key(set_api_key, ctx, query):
234
+ return True
235
+
236
+ if handle_set_config(config_str, ctx, query):
237
+ return True
238
+
239
+ if handle_history(history_flag, history_count, ctx, query):
240
+ return True
241
+
242
+ return False
@@ -0,0 +1,119 @@
1
+ """
2
+ History management functions for Janito CLI.
3
+ """
4
+ import sys
5
+ import json
6
+ import datetime
7
+ from pathlib import Path
8
+ from typing import Optional, List, Dict, Any
9
+ import typer
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+
13
+ from janito.config import get_config
14
+
15
+ console = Console()
16
+
17
+ def handle_history(history_flag: bool, history_count: Optional[int], ctx: typer.Context, query: Optional[str]) -> bool:
18
+ """
19
+ Handle the --history parameter to display conversation history.
20
+
21
+ Args:
22
+ history_flag: Whether to show history (--history flag)
23
+ history_count: Number of history entries to display (value after --history)
24
+ ctx: Typer context
25
+ query: Query string
26
+
27
+ Returns:
28
+ bool: True if the program should exit after this operation
29
+ """
30
+ # Check if --history was used
31
+ if history_flag:
32
+ try:
33
+ # If --history is used with a count value passed from app.py, use that
34
+ # If no count is specified, default to 20
35
+ count = 20 if history_count is None else history_count
36
+
37
+ # Get the workspace directory
38
+ workspace_dir = Path(get_config().workspace_dir)
39
+ janito_dir = workspace_dir / ".janito"
40
+ messages_dir = janito_dir / "last_messages"
41
+
42
+ if not messages_dir.exists() or not any(messages_dir.iterdir()):
43
+ console.print("[bold yellow]⚠️ No conversation history found[/bold yellow]")
44
+ return True # Always exit after displaying history
45
+
46
+ # Find all message files and sort by timestamp (newest first)
47
+ message_files = [f for f in messages_dir.iterdir() if f.is_file() and f.suffix == '.json']
48
+ message_files.sort(key=lambda x: x.stem, reverse=True)
49
+
50
+ # Limit to the requested number of entries
51
+ message_files = message_files[:count]
52
+
53
+ # Create a table for the history
54
+ table = Table(title=f"Conversation History (Last {min(count, len(message_files))} Entries)")
55
+ table.add_column("ID", style="cyan")
56
+ table.add_column("Date", style="green")
57
+ table.add_column("Time", style="green")
58
+ table.add_column("First Query", style="yellow")
59
+
60
+ # Add rows to the table
61
+ for file in message_files:
62
+ try:
63
+ with open(file, "r", encoding="utf-8") as f:
64
+ message_object = json.load(f)
65
+
66
+ # Extract message ID and timestamp
67
+ message_id = message_object.get("id", file.stem)
68
+
69
+ # Parse timestamp
70
+ timestamp_str = message_object.get("timestamp")
71
+ if timestamp_str:
72
+ timestamp = datetime.datetime.fromisoformat(timestamp_str)
73
+ date_str = timestamp.strftime("%Y-%m-%d")
74
+ time_str = timestamp.strftime("%H:%M:%S")
75
+ else:
76
+ # Fallback to file name which is a timestamp
77
+ timestamp_str = file.stem
78
+ date_str = timestamp_str[:8] # YYYYMMDD
79
+ time_str = timestamp_str[8:] # HHMMSS
80
+
81
+ # Format the date and time
82
+ if len(date_str) == 8:
83
+ date_str = f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:8]}"
84
+ if len(time_str) == 6:
85
+ time_str = f"{time_str[:2]}:{time_str[2:4]}:{time_str[4:6]}"
86
+
87
+ # Extract the first user message
88
+ messages = message_object.get("messages", [])
89
+ first_query = "N/A"
90
+ for msg in messages:
91
+ if msg.get("role") == "user":
92
+ first_query = msg.get("content", "N/A")
93
+ # Truncate long queries
94
+ if len(first_query) > 60:
95
+ first_query = first_query[:57] + "..."
96
+ break
97
+
98
+ table.add_row(message_id, date_str, time_str, first_query)
99
+ except Exception as e:
100
+ table.add_row(file.stem, "Error", "Error", f"Failed to parse: {str(e)}")
101
+
102
+ console.print(table)
103
+
104
+ # Display information about how to continue conversations
105
+ console.print("\n[bold blue]💡 To continue a conversation:[/bold blue]")
106
+ script_name = "janito"
107
+ if sys.argv[0].endswith(('janito', 'janito.exe')):
108
+ console.print(f" {script_name} --continue <ID> <request>")
109
+ else:
110
+ console.print(f" python -m janito --continue <ID> <request>")
111
+
112
+ # If --history flag is used, always exit regardless of whether a query is provided
113
+ return True
114
+
115
+ except Exception as e:
116
+ console.print(f"[bold red]Error displaying history:[/bold red] {str(e)}")
117
+ return True # Exit on error
118
+
119
+ return False
@@ -0,0 +1,72 @@
1
+ """
2
+ Profile and role management functions for Janito CLI.
3
+ """
4
+ import sys
5
+ from typing import Optional
6
+ import typer
7
+ from rich.console import Console
8
+
9
+ from janito.config import get_config
10
+
11
+ console = Console()
12
+
13
+ def handle_profile(profile: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
14
+ """
15
+ Handle the --profile parameter.
16
+
17
+ Args:
18
+ profile: Profile name
19
+ ctx: Typer context
20
+ query: Query string
21
+
22
+ Returns:
23
+ bool: True if the program should exit after this operation
24
+ """
25
+ if profile is not None:
26
+ try:
27
+ # Apply profile without saving to config
28
+ config = get_config()
29
+ profile_data = config.get_available_profiles()[profile.lower()]
30
+
31
+ # Set values directly without saving
32
+ config._temperature = profile_data["temperature"]
33
+ config._profile = profile.lower()
34
+
35
+ console.print(f"[bold green]✅ Profile '{profile.lower()}' applied for this session only[/bold green]")
36
+ console.print(f"[dim]📝 Description: {profile_data['description']}[/dim]")
37
+
38
+ # Exit after applying profile if no other operation is requested
39
+ return ctx.invoked_subcommand is None and not query
40
+ except ValueError as e:
41
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
42
+ sys.exit(1)
43
+
44
+ return False
45
+
46
+ def handle_role(role: Optional[str], ctx: typer.Context, query: Optional[str]) -> bool:
47
+ """
48
+ Handle the --role parameter.
49
+
50
+ Args:
51
+ role: Role name
52
+ ctx: Typer context
53
+ query: Query string
54
+
55
+ Returns:
56
+ bool: True if the program should exit after this operation
57
+ """
58
+ if role is not None:
59
+ try:
60
+ # Set role directly without saving to config
61
+ config = get_config()
62
+ config._role = role
63
+
64
+ console.print(f"[bold green]✅ Role '{role}' applied for this session only[/bold green]")
65
+
66
+ # Exit after applying role if no other operation is requested
67
+ return ctx.invoked_subcommand is None and not query
68
+ except Exception as e:
69
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
70
+ sys.exit(1)
71
+
72
+ return False
@@ -0,0 +1,24 @@
1
+ """
2
+ Parameter validation functions for Janito CLI.
3
+ """
4
+ import sys
5
+ from rich.console import Console
6
+
7
+ console = Console()
8
+
9
+ def validate_parameters(temperature: float) -> None:
10
+ """
11
+ Validate temperature parameter.
12
+
13
+ Args:
14
+ temperature: Temperature value for model generation
15
+ """
16
+ try:
17
+ if temperature < 0.0 or temperature > 1.0:
18
+ raise ValueError("Temperature must be between 0.0 and 1.0")
19
+
20
+ # We'll use this value directly in the agent initialization but we don't save it to config
21
+ # Temperature display is hidden
22
+ except ValueError as e:
23
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
24
+ sys.exit(1)
@@ -0,0 +1,31 @@
1
+ """
2
+ Workspace management functions for Janito CLI.
3
+ """
4
+ import sys
5
+ from typing import Optional
6
+ from rich.console import Console
7
+
8
+ from janito.config import get_config
9
+
10
+ console = Console()
11
+
12
+ def handle_workspace(workspace: Optional[str]) -> bool:
13
+ """
14
+ Handle the --workspace parameter.
15
+
16
+ Args:
17
+ workspace: Workspace directory path
18
+
19
+ Returns:
20
+ bool: True if the program should exit after this operation
21
+ """
22
+ if workspace:
23
+ try:
24
+ console.print(f"[bold]📂 Setting workspace directory to: {workspace}[/bold]")
25
+ get_config().workspace_dir = workspace
26
+ console.print(f"[bold green]✅ Workspace directory set to: {get_config().workspace_dir}[/bold green]")
27
+ except ValueError as e:
28
+ console.print(f"[bold red]Error:[/bold red] {str(e)}")
29
+ sys.exit(1)
30
+
31
+ return False