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/__init__.py CHANGED
@@ -2,4 +2,4 @@
2
2
  Janito package.
3
3
  """
4
4
 
5
- __version__ = "0.11.0"
5
+ __version__ = "0.12.0"
janito/__main__.py CHANGED
@@ -1,205 +1,7 @@
1
- """
2
- Main entry point for Janito.
3
- """
4
- import os
5
- import sys
6
- from typing import Optional
7
- import importlib.resources
8
- import typer
9
- from rich.console import Console
10
- import anthropic
11
- from janito.config import get_config
12
- from janito.chat_history import store_conversation, get_chat_history_context
13
- from janito.callbacks import pre_tool_callback, post_tool_callback, text_callback
14
- from janito.token_report import generate_token_report
15
- from janito.tools import str_replace_editor
16
- from janito.tools.bash import bash_tool
17
- import claudine
18
-
19
- app = typer.Typer()
20
-
21
- @app.command()
22
- def create_tool(name: str = typer.Argument(..., help="Name of the tool to create")):
23
- """
24
- Create a new tool with the given name.
25
- """
26
- console = Console()
27
-
28
- # Ensure workspace is set
29
- workspace_dir = get_config().workspace_dir
30
-
31
- # Create the tools directory if it doesn't exist
32
- tools_dir = os.path.join(workspace_dir, "tools")
33
- os.makedirs(tools_dir, exist_ok=True)
34
-
35
- # Create the tool file
36
- tool_file = os.path.join(tools_dir, f"{name}.py")
37
-
38
- # Check if the file already exists
39
- if os.path.exists(tool_file):
40
- console.print(f"[bold red]Error:[/bold red] Tool file already exists: {tool_file}")
41
- return
42
-
43
- # Create the tool file with a template
44
- template = f'''"""
45
- {name} tool for Janito.
46
- """
47
-
48
- def {name}(param1: str) -> str:
49
- """
50
- Description of the {name} tool.
51
-
52
- Args:
53
- param1: Description of param1
54
-
55
- Returns:
56
- str: Description of return value
57
- """
58
- # TODO: Implement the tool
59
- return f"Executed {name} with param1={{param1}}"
60
- '''
61
-
62
- with open(tool_file, "w") as f:
63
- f.write(template)
64
-
65
- console.print(f"[bold green]Created tool:[/bold green] {tool_file}")
66
-
67
- @app.callback(invoke_without_command=True)
68
- def main(ctx: typer.Context,
69
- query: Optional[str] = typer.Argument(None, help="Query to send to the claudine agent"),
70
- verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose mode with detailed output"),
71
- show_tokens: bool = typer.Option(False, "--show-tokens", "-t", help="Show detailed token usage and pricing information"),
72
- workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Set the workspace directory"),
73
- config_str: Optional[str] = typer.Option(None, "--set-config", help="Configuration string in format 'key=value', e.g., 'context=5' for number of history messages to include"),
74
- show_config: bool = typer.Option(False, "--show-config", help="Show current configuration")):
75
- """
76
- Janito CLI tool. If a query is provided without a command, it will be sent to the claudine agent.
77
- """
78
- console = Console()
79
-
80
- # Set verbose mode in config
81
- get_config().verbose = verbose
82
-
83
- if workspace:
84
- try:
85
- print(f"Setting workspace directory to: {workspace}")
86
- get_config().workspace_dir = workspace
87
- print(f"Workspace directory set to: {get_config().workspace_dir}")
88
- except ValueError as e:
89
- console.print(f"[bold red]Error:[/bold red] {str(e)}")
90
- sys.exit(1)
91
-
92
- # Show current configuration if requested
93
- if show_config:
94
- config = get_config()
95
- console.print("[bold blue]Current Configuration:[/bold blue]")
96
- console.print(f"[bold]Workspace Directory:[/bold] {config.workspace_dir}")
97
- console.print(f"[bold]Verbose Mode:[/bold] {'Enabled' if config.verbose else 'Disabled'}")
98
- console.print(f"[bold]Chat History Context Count:[/bold] {config.history_context_count} messages")
99
- # Exit if this was the only operation requested
100
- if ctx.invoked_subcommand is None and not query:
101
- sys.exit(0)
102
-
103
- # Handle the --set-config parameter
104
- if config_str is not None:
105
- try:
106
- # Parse the config string
107
- if "context=" in config_str:
108
- context_value = config_str.split("context=")[1].strip()
109
- # If there are other configs after context, extract just the number
110
- if " " in context_value:
111
- context_value = context_value.split(" ")[0]
112
-
113
- try:
114
- context_value = int(context_value)
115
- if context_value < 0:
116
- console.print("[bold red]Error:[/bold red] History context count must be a non-negative integer")
117
- return
118
-
119
- get_config().history_context_count = context_value
120
- console.print(f"[bold green]Chat history context count set to {context_value} messages[/bold green]")
121
- except ValueError:
122
- console.print(f"[bold red]Error:[/bold red] Invalid context value: {context_value}. Must be an integer.")
123
- else:
124
- console.print(f"[bold yellow]Warning:[/bold yellow] Unsupported configuration in: {config_str}")
125
- except Exception as e:
126
- console.print(f"[bold red]Error:[/bold red] {str(e)}")
127
-
128
- if ctx.invoked_subcommand is None:
129
- # If no query provided in command line, read from stdin
130
- if not query:
131
- console.print("[bold blue]No query provided in command line. Reading from stdin...[/bold blue]")
132
- query = sys.stdin.read().strip()
133
-
134
- # Only proceed if we have a query (either from command line or stdin)
135
- if query:
136
- # Get API key from environment variable or ask the user
137
- api_key = os.environ.get("ANTHROPIC_API_KEY")
138
- if not api_key:
139
- console.print("[bold yellow]Warning:[/bold yellow] ANTHROPIC_API_KEY environment variable not set.")
140
- console.print("Please set it or provide your API key now:")
141
- api_key = typer.prompt("Anthropic API Key", hide_input=True)
142
-
143
- # Load instructions from file
144
- import importlib.resources as pkg_resources
145
- try:
146
- # For Python 3.9+
147
- try:
148
- from importlib.resources import files
149
- instructions = files('janito.data').joinpath('instructions.txt').read_text(encoding='utf-8')
150
- # Fallback for older Python versions
151
- except (ImportError, AttributeError):
152
- instructions = pkg_resources.read_text('janito.data', 'instructions.txt', encoding='utf-8')
153
- except Exception as e:
154
- console.print(f"[bold red]Error loading instructions:[/bold red] {str(e)}")
155
- instructions = "You are Janito, an AI assistant."
156
-
157
- # Temporarily disable chat history
158
- # Get chat history context
159
- # chat_history = get_chat_history_context(get_config().history_context_count)
160
- # if chat_history:
161
- # console.print("[dim]Loaded chat history from previous sessions.[/dim]")
162
- # # Append chat history to instructions
163
- # instructions = f"{instructions}\n\n{chat_history}"
164
-
165
- # Get tools
166
- from janito.tools import get_tools
167
- tools_list = get_tools()
168
-
169
- # Initialize the agent with the tools
170
- agent = claudine.Agent(
171
- api_key=api_key,
172
- system_prompt=instructions,
173
- callbacks={"pre_tool": pre_tool_callback, "post_tool": post_tool_callback, "text": text_callback},
174
- text_editor_tool=str_replace_editor,
175
- #bash_tool=bash_tool,
176
- tools=tools_list,
177
- verbose=verbose
178
- )
179
-
180
- # Send the query to the agent
181
- try:
182
- agent.query(query)
183
-
184
- # Temporarily disable storing conversation in chat history
185
- # Store the conversation in chat history
186
- # store_conversation(query, response, agent)
187
-
188
- # Print token usage report if show_tokens mode is enabled
189
- if show_tokens:
190
- generate_token_report(agent, verbose=True)
191
- else:
192
- # Show basic token usage
193
- generate_token_report(agent, verbose=False)
194
-
195
- except anthropic.APIError as e:
196
- console.print(f"[bold red]Anthropic API Error:[/bold red] {str(e)}")
197
-
198
- except Exception as e:
199
- console.print(f"[bold red]Error:[/bold red] {str(e)}")
200
- if verbose:
201
- import traceback
202
- console.print(traceback.format_exc())
203
-
204
- if __name__ == "__main__":
1
+ """
2
+ Main entry point for Janito.
3
+ """
4
+ from janito.cli import app
5
+
6
+ if __name__ == "__main__":
205
7
  app()
janito/callbacks.py CHANGED
@@ -1,132 +1,34 @@
1
- """
2
- Callback functions for tool execution in janito.
3
- """
4
-
5
- from typing import Dict, Any, Tuple, Optional, List
6
- from rich.console import Console
7
- from rich.markdown import Markdown
8
-
9
- from janito.config import get_config
10
- from janito.tools import find_files
11
- from janito.tools.str_replace_editor.editor import str_replace_editor
12
- from janito.tools.delete_file import delete_file
13
- from janito.tools.search_text import search_text
14
- from janito.tools.decorators import format_tool_label
15
-
16
- # Note: ConsoleCallback has been removed as we're using pre_tool and post_tool callbacks directly
17
-
18
- def pre_tool_callback(tool_name: str, tool_input: Dict[str, Any]) -> Tuple[Dict[str, Any], bool]:
19
- """
20
- Callback function that runs before a tool is executed.
21
-
22
- Args:
23
- tool_name: Name of the tool being called
24
- tool_input: Input parameters for the tool
25
-
26
- Returns:
27
- Tuple of (modified tool input, whether to cancel the tool call)
28
- """
29
- console = Console()
30
-
31
- # Add debug counter only when verbose mode is enabled
32
- if get_config().verbose:
33
- if not hasattr(pre_tool_callback, "counter"):
34
- pre_tool_callback.counter = 1
35
- console.print(f"[bold green]DEBUG: Starting tool call #{pre_tool_callback.counter}[/bold green]")
36
-
37
- # Print the tool name and input
38
- console.print(f"[bold green]Tool:[/bold green] {tool_name}")
39
- console.print(f"[bold green]Input:[/bold green] {tool_input}")
40
- else:
41
- # For non-debug mode, just print a simple message
42
- # Find the tool function
43
- tool_func = None
44
- if tool_name == "find_files":
45
- tool_func = find_files
46
- elif tool_name == "str_replace_editor":
47
- tool_func = str_replace_editor
48
- elif tool_name == "delete_file":
49
- tool_func = delete_file
50
- elif tool_name == "search_text":
51
- tool_func = search_text
52
-
53
- # Format the input for display
54
- display_input = ""
55
- if "path" in tool_input:
56
- display_input = tool_input["path"]
57
- elif "file_path" in tool_input:
58
- display_input = tool_input["file_path"]
59
-
60
- # Print formatted tool label if available
61
- formatted_label = format_tool_label(tool_func, tool_input)
62
- if formatted_label:
63
- console.print("[bold cyan] Tool:[/bold cyan]", formatted_label, end=" → ")
64
- else:
65
- console.print("[bold cyan] Tool:[/bold cyan]", f"{tool_name} {display_input}", end=" → ")
66
-
67
- return tool_input, True # Continue with the tool call
68
-
69
- def post_tool_callback(tool_name: str, tool_input: Dict[str, Any], result: Any) -> Any:
70
- """
71
- Callback function that runs after a tool is executed.
72
-
73
- Args:
74
- tool_name: Name of the tool that was called
75
- tool_input: Input parameters for the tool
76
- result: Result of the tool call
77
-
78
- Returns:
79
- Modified result
80
- """
81
- console = Console()
82
-
83
- # Add debug counter only when verbose mode is enabled
84
- if get_config().verbose:
85
- if not hasattr(post_tool_callback, "counter"):
86
- post_tool_callback.counter = 1
87
- console.print(f"[bold green]DEBUG: Completed tool call #{post_tool_callback.counter}[/bold green]")
88
- post_tool_callback.counter += 1
89
-
90
- # Show the number of lines in the result content
91
- if isinstance(result, tuple) and len(result) >= 1:
92
- content, is_error = result
93
- # Define prefix icon based on is_error
94
- icon_prefix = "❌ " if is_error else "✅ "
95
-
96
- if isinstance(content, str):
97
- # Count the number of lines in the content
98
- line_count = content.count('\n') + 1 if content else 0
99
- console.print(f"{icon_prefix}{line_count} items")
100
- else:
101
- console.print(f"{icon_prefix}{content}")
102
- else:
103
- # If result is not a tuple, convert to string and count lines
104
- result_str = str(result)
105
- # Default to success icon when no error status is available
106
- icon_prefix = "✅ "
107
- line_count = result_str.count('\n') + 1 if result_str else 0
108
- console.print(f"{icon_prefix}{line_count} lines")
109
-
110
- return result
111
-
112
- def text_callback(text: str) -> None:
113
- """
114
- Callback function that handles text output from the agent.
115
-
116
- Args:
117
- text: Text output from the agent
118
-
119
- Returns:
120
- None
121
- """
122
- console = Console()
123
-
124
- # Add debug counter only when debug mode is enabled
125
- if get_config().debug_mode:
126
- if not hasattr(text_callback, "counter"):
127
- text_callback.counter = 1
128
- console.print(f"[bold blue]DEBUG: Text callback #{text_callback.counter}[/bold blue]")
129
- text_callback.counter += 1
130
-
131
- # Print the text with markdown formatting
132
- console.print("[bold magenta]Janito:[/bold magenta] ", Markdown(text, code_theme="monokai"), end="")
1
+ """
2
+ Callback functions for tool execution in janito.
3
+ """
4
+
5
+ from rich.console import Console
6
+ from rich.markdown import Markdown
7
+
8
+ from janito.config import get_config
9
+
10
+ # Counter for pre-tool callbacks
11
+ pre_tool_callbacks = 0
12
+
13
+ def text_callback(text: str) -> None:
14
+ """
15
+ Callback function that handles text output from the agent.
16
+
17
+ Args:
18
+ text: Text output from the agent
19
+
20
+ Returns:
21
+ None
22
+ """
23
+ console = Console()
24
+
25
+ # Add debug counter only when debug mode is enabled
26
+ if get_config().debug_mode:
27
+ if not hasattr(text_callback, "counter"):
28
+ text_callback.counter = 1
29
+ console.print(f"[bold blue]DEBUG: Text callback #{text_callback.counter}[/bold blue]")
30
+ text_callback.counter += 1
31
+
32
+ # Print the text with markdown formatting
33
+ console.print(Markdown(text, code_theme="monokai"), end="")
34
+
janito/cli/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """
2
+ CLI module for Janito.
3
+ """
4
+ from janito.cli.app import app
5
+
6
+ __all__ = ["app"]
janito/cli/agent.py ADDED
@@ -0,0 +1,287 @@
1
+ """
2
+ Agent initialization and query handling for Janito CLI.
3
+ """
4
+ import os
5
+ import sys
6
+ import json
7
+ import anthropic
8
+ import claudine
9
+ import typer
10
+ from rich.console import Console
11
+ from pathlib import Path
12
+ from jinja2 import Template
13
+ import importlib.resources as pkg_resources
14
+
15
+ from janito.config import get_config, Config
16
+ from janito.callbacks import text_callback
17
+ from janito.token_report import generate_token_report
18
+ from janito.tools import str_replace_editor
19
+ from janito.tools.bash.bash import bash_tool
20
+ from janito.cli.output import display_generation_params
21
+
22
+ console = Console()
23
+
24
+ def get_api_key() -> str:
25
+ """
26
+ Get the API key from global config, environment variable, or user input.
27
+
28
+ Returns:
29
+ str: The API key
30
+ """
31
+ # Get API key from global config, environment variable, or ask the user
32
+ api_key = Config.get_api_key()
33
+
34
+ # If not found in global config, try environment variable
35
+ if not api_key:
36
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
37
+
38
+ # If still not found, prompt the user
39
+ if not api_key:
40
+ console.print("[bold yellow]⚠️ Warning:[/bold yellow] API key not found in global config or ANTHROPIC_API_KEY environment variable.")
41
+ console.print("🔑 Please set it using --set-api-key or provide your API key now:")
42
+ api_key = typer.prompt("Anthropic API Key", hide_input=True)
43
+
44
+ return api_key
45
+
46
+ def load_instructions() -> str:
47
+ """
48
+ Load instructions template and render it with variables.
49
+
50
+ Returns:
51
+ str: The rendered instructions
52
+ """
53
+ import platform
54
+
55
+ try:
56
+ # For Python 3.9+
57
+ try:
58
+ from importlib.resources import files
59
+ template_content = files('janito.data').joinpath('instructions_template.txt').read_text(encoding='utf-8')
60
+ # Fallback for older Python versions
61
+ except (ImportError, AttributeError):
62
+ template_content = pkg_resources.read_text('janito.data', 'instructions_template.txt', encoding='utf-8')
63
+
64
+ # Create template variables
65
+ template_variables = {
66
+ 'platform': platform.system(),
67
+ 'role': get_config().role,
68
+ # Add any other variables you want to pass to the template here
69
+ }
70
+
71
+ # Create template and render
72
+ template = Template(template_content)
73
+ instructions = template.render(**template_variables)
74
+
75
+ except Exception as e:
76
+ console.print(f"[bold red]❌ Error loading instructions template:[/bold red] {str(e)}")
77
+ # Try to fall back to regular instructions.txt
78
+ try:
79
+ # For Python 3.9+
80
+ try:
81
+ from importlib.resources import files
82
+ instructions = files('janito.data').joinpath('instructions.txt').read_text(encoding='utf-8')
83
+ # Fallback for older Python versions
84
+ except (ImportError, AttributeError):
85
+ instructions = pkg_resources.read_text('janito.data', 'instructions.txt', encoding='utf-8')
86
+ except Exception as e2:
87
+ console.print(f"[bold red]❌ Error loading fallback instructions:[/bold red] {str(e2)}")
88
+ instructions = "You are Janito, an AI assistant."
89
+
90
+ return instructions
91
+
92
+ def initialize_agent(temperature: float, verbose: bool) -> claudine.Agent:
93
+ """
94
+ Initialize the Claude agent with tools and configuration.
95
+
96
+ Args:
97
+ temperature: Temperature value for model generation
98
+ verbose: Whether to enable verbose mode
99
+
100
+ Returns:
101
+ claudine.Agent: The initialized agent
102
+ """
103
+ # Get API key
104
+ api_key = get_api_key()
105
+
106
+ # Load instructions
107
+ instructions = load_instructions()
108
+
109
+ # Get tools
110
+ from janito.tools import get_tools, reset_tracker
111
+ tools_list = get_tools()
112
+
113
+ # Reset usage tracker before each query
114
+ reset_tracker()
115
+
116
+ # Use command line parameters if provided (not default values), otherwise use config
117
+ temp_to_use = temperature if temperature != 0.0 else get_config().temperature
118
+
119
+ # Get profile parameters if a profile is set
120
+ config = get_config()
121
+ profile_data = None
122
+ if config.profile:
123
+ profile_data = config.get_available_profiles()[config.profile]
124
+
125
+ # Display generation parameters if verbose mode is enabled
126
+ if verbose:
127
+ display_generation_params(temp_to_use, profile_data, temperature)
128
+
129
+ # Create config_params dictionary with generation parameters
130
+ config_params = {
131
+ "temperature": temp_to_use
132
+ }
133
+
134
+ # Add top_k and top_p from profile if available
135
+ if profile_data:
136
+ if "top_k" in profile_data and profile_data["top_k"] != 0:
137
+ config_params["top_k"] = profile_data["top_k"]
138
+ if "top_p" in profile_data and profile_data["top_p"] != 0.0:
139
+ config_params["top_p"] = profile_data["top_p"]
140
+
141
+ # Initialize the agent
142
+ agent = claudine.Agent(
143
+ api_key=api_key,
144
+ system_prompt=instructions,
145
+ callbacks={"text": text_callback},
146
+ text_editor_tool=str_replace_editor,
147
+ bash_tool=bash_tool,
148
+ tools=tools_list,
149
+ verbose=verbose,
150
+ max_tokens=8126,
151
+ max_tool_rounds=100,
152
+ config_params=config_params,
153
+ )
154
+
155
+ return agent
156
+
157
+ def save_messages(agent):
158
+ """
159
+ Save agent messages to .janito/last_message.json
160
+
161
+ Args:
162
+ agent: The claudine agent instance
163
+ """
164
+ try:
165
+ # Get the workspace directory
166
+ workspace_dir = Path(get_config().workspace_dir)
167
+
168
+ # Create .janito directory if it doesn't exist
169
+ janito_dir = workspace_dir / ".janito"
170
+ janito_dir.mkdir(exist_ok=True)
171
+
172
+ # Get messages from the agent
173
+ messages = agent.get_messages()
174
+
175
+ # Save messages to file
176
+ with open(janito_dir / "last_message.json", "w", encoding="utf-8") as f:
177
+ json.dump(messages, f, ensure_ascii=False, indent=2)
178
+
179
+ if get_config().verbose:
180
+ console.print(f"[bold green]✅ Conversation saved to {janito_dir / 'last_message.json'}[/bold green]")
181
+ except Exception as e:
182
+ console.print(f"[bold red]❌ Error saving conversation:[/bold red] {str(e)}")
183
+
184
+ def load_messages():
185
+ """
186
+ Load messages from .janito/last_message.json
187
+
188
+ Returns:
189
+ List of message dictionaries or None if file doesn't exist
190
+ """
191
+ try:
192
+ # Get the workspace directory
193
+ workspace_dir = Path(get_config().workspace_dir)
194
+
195
+ # Check if file exists
196
+ messages_file = workspace_dir / ".janito" / "last_message.json"
197
+ if not messages_file.exists():
198
+ console.print("[bold yellow]⚠️ No previous conversation found[/bold yellow]")
199
+ return None
200
+
201
+ # Load messages from file
202
+ with open(messages_file, "r", encoding="utf-8") as f:
203
+ messages = json.load(f)
204
+
205
+ if get_config().verbose:
206
+ console.print(f"[bold green]✅ Loaded previous conversation from {messages_file}[/bold green]")
207
+ console.print(f"[dim]📝 Conversation has {len(messages)} messages[/dim]")
208
+
209
+ return messages
210
+ except Exception as e:
211
+ console.print(f"[bold red]❌ Error loading previous conversation:[/bold red] {str(e)}")
212
+ return None
213
+
214
+ def handle_query(query: str, temperature: float, verbose: bool, show_tokens: bool, continue_conversation: bool = False) -> None:
215
+ """
216
+ Handle a query by initializing the agent and sending the query.
217
+
218
+ Args:
219
+ query: The query to send to the agent
220
+ temperature: Temperature value for model generation
221
+ verbose: Whether to enable verbose mode
222
+ show_tokens: Whether to show detailed token usage
223
+ continue_conversation: Whether to continue the previous conversation
224
+ """
225
+ # Initialize the agent
226
+ agent = initialize_agent(temperature, verbose)
227
+
228
+ # Load previous messages if continuing conversation
229
+ if continue_conversation:
230
+ messages = load_messages()
231
+ if messages:
232
+ agent.set_messages(messages)
233
+ console.print("[bold blue]🔄 Continuing previous conversation[/bold blue]")
234
+
235
+ # Send the query to the agent
236
+ try:
237
+ agent.query(query)
238
+
239
+ # Save messages after successful query
240
+ save_messages(agent)
241
+
242
+ # Print token usage report
243
+ if show_tokens:
244
+ generate_token_report(agent, verbose=True, interrupted=False)
245
+ else:
246
+ # Show basic token usage
247
+ generate_token_report(agent, verbose=False, interrupted=False)
248
+
249
+ # Print tool usage statistics
250
+ from janito.tools import print_usage_stats
251
+ print_usage_stats()
252
+
253
+ except KeyboardInterrupt:
254
+ # Handle Ctrl+C by printing token and tool usage information
255
+ console.print("\n[bold yellow]⚠️ Query interrupted by user (Ctrl+C)[/bold yellow]")
256
+
257
+ # Save messages even if interrupted
258
+ save_messages(agent)
259
+
260
+ # Print token usage report (even if interrupted)
261
+ try:
262
+ if show_tokens:
263
+ generate_token_report(agent, verbose=True, interrupted=True)
264
+ else:
265
+ # Show basic token usage
266
+ generate_token_report(agent, verbose=False, interrupted=True)
267
+
268
+ # Print tool usage statistics
269
+ from janito.tools import print_usage_stats
270
+ print_usage_stats()
271
+ except Exception as e:
272
+ console.print(f"[bold red]❌ Error generating usage report:[/bold red] {str(e)}")
273
+ if verbose:
274
+ import traceback
275
+ console.print(traceback.format_exc())
276
+
277
+ # Exit with non-zero status to indicate interruption
278
+ sys.exit(130) # 130 is the standard exit code for SIGINT
279
+
280
+ except anthropic.APIError as e:
281
+ console.print(f"[bold red]❌ Anthropic API Error:[/bold red] {str(e)}")
282
+
283
+ except Exception as e:
284
+ console.print(f"[bold red]❌ Error:[/bold red] {str(e)}")
285
+ if verbose:
286
+ import traceback
287
+ console.print(traceback.format_exc())