janito 0.12.0__py3-none-any.whl → 0.13.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 CHANGED
@@ -2,4 +2,4 @@
2
2
  Janito package.
3
3
  """
4
4
 
5
- __version__ = "0.12.0"
5
+ __version__ = "0.13.0"
janito/cli/agent.py CHANGED
@@ -7,6 +7,8 @@ import json
7
7
  import anthropic
8
8
  import claudine
9
9
  import typer
10
+ import datetime
11
+ from typing import Optional
10
12
  from rich.console import Console
11
13
  from pathlib import Path
12
14
  from jinja2 import Template
@@ -19,6 +21,7 @@ from janito.tools import str_replace_editor
19
21
  from janito.tools.bash.bash import bash_tool
20
22
  from janito.cli.output import display_generation_params
21
23
 
24
+
22
25
  console = Console()
23
26
 
24
27
  def get_api_key() -> str:
@@ -154,12 +157,25 @@ def initialize_agent(temperature: float, verbose: bool) -> claudine.Agent:
154
157
 
155
158
  return agent
156
159
 
160
+ def generate_message_id():
161
+ """
162
+ Generate a message ID based on timestamp with seconds granularity
163
+
164
+ Returns:
165
+ str: A timestamp-based message ID
166
+ """
167
+ timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
168
+ return timestamp
169
+
157
170
  def save_messages(agent):
158
171
  """
159
- Save agent messages to .janito/last_message.json
172
+ Save agent messages to .janito/last_messages/{message_id}.json
160
173
 
161
174
  Args:
162
175
  agent: The claudine agent instance
176
+
177
+ Returns:
178
+ str: The message ID used for saving
163
179
  """
164
180
  try:
165
181
  # Get the workspace directory
@@ -169,49 +185,115 @@ def save_messages(agent):
169
185
  janito_dir = workspace_dir / ".janito"
170
186
  janito_dir.mkdir(exist_ok=True)
171
187
 
188
+ # Create last_messages directory if it doesn't exist
189
+ messages_dir = janito_dir / "last_messages"
190
+ messages_dir.mkdir(exist_ok=True)
191
+
192
+ # Generate a unique message ID
193
+ message_id = generate_message_id()
194
+
172
195
  # Get messages from the agent
173
196
  messages = agent.get_messages()
174
197
 
198
+ # Create a message object with metadata
199
+ message_object = {
200
+ "id": message_id,
201
+ "timestamp": datetime.datetime.now().isoformat(),
202
+ "messages": messages
203
+ }
204
+
175
205
  # 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)
206
+ message_file = messages_dir / f"{message_id}.json"
207
+ with open(message_file, "w", encoding="utf-8") as f:
208
+ json.dump(message_object, f, ensure_ascii=False, indent=2)
209
+
210
+ # No longer saving to last_message.json for backward compatibility
178
211
 
179
212
  if get_config().verbose:
180
- console.print(f"[bold green]✅ Conversation saved to {janito_dir / 'last_message.json'}[/bold green]")
213
+ console.print(f"[bold green]✅ Conversation saved to {message_file}[/bold green]")
214
+
215
+ return message_id
181
216
  except Exception as e:
182
217
  console.print(f"[bold red]❌ Error saving conversation:[/bold red] {str(e)}")
218
+ return None
183
219
 
184
- def load_messages():
220
+ def load_messages(message_id=None):
185
221
  """
186
- Load messages from .janito/last_message.json
222
+ Load messages from .janito/last_messages/{message_id}.json or the latest message file
187
223
 
224
+ Args:
225
+ message_id: Optional message ID to load specific conversation
226
+
188
227
  Returns:
189
228
  List of message dictionaries or None if file doesn't exist
190
229
  """
191
230
  try:
192
231
  # Get the workspace directory
193
232
  workspace_dir = Path(get_config().workspace_dir)
233
+ janito_dir = workspace_dir / ".janito"
234
+ messages_dir = janito_dir / "last_messages"
235
+
236
+ # If message_id is provided, try to load that specific conversation
237
+ if message_id:
238
+ # Check if the message ID is a file name or just the ID
239
+ if message_id.endswith('.json'):
240
+ message_file = messages_dir / message_id
241
+ else:
242
+ message_file = messages_dir / f"{message_id}.json"
243
+
244
+ if not message_file.exists():
245
+ console.print(f"[bold yellow]⚠️ No conversation found with ID {message_id}[/bold yellow]")
246
+ return None
247
+
248
+ # Load messages from file
249
+ with open(message_file, "r", encoding="utf-8") as f:
250
+ message_object = json.load(f)
251
+
252
+ # Extract messages from the message object
253
+ if isinstance(message_object, dict) and "messages" in message_object:
254
+ messages = message_object["messages"]
255
+ else:
256
+ # Handle legacy format
257
+ messages = message_object
258
+
259
+ if get_config().verbose:
260
+ console.print(f"[bold green]✅ Loaded conversation from {message_file}[/bold green]")
261
+ console.print(f"[dim]📝 Conversation has {len(messages)} messages[/dim]")
262
+
263
+ return messages
194
264
 
195
- # Check if file exists
196
- messages_file = workspace_dir / ".janito" / "last_message.json"
197
- if not messages_file.exists():
265
+ # If no message_id is provided, try to load the latest message from last_messages directory
266
+ if not messages_dir.exists() or not any(messages_dir.iterdir()):
198
267
  console.print("[bold yellow]⚠️ No previous conversation found[/bold yellow]")
199
268
  return None
200
269
 
201
- # Load messages from file
202
- with open(messages_file, "r", encoding="utf-8") as f:
203
- messages = json.load(f)
270
+ # Find the latest message file (based on filename which is a timestamp)
271
+ latest_file = max(
272
+ [f for f in messages_dir.iterdir() if f.is_file() and f.suffix == '.json'],
273
+ key=lambda x: x.stem
274
+ )
275
+
276
+ # Load messages from the latest file
277
+ with open(latest_file, "r", encoding="utf-8") as f:
278
+ message_object = json.load(f)
279
+
280
+ # Extract messages from the message object
281
+ if isinstance(message_object, dict) and "messages" in message_object:
282
+ messages = message_object["messages"]
283
+ else:
284
+ # Handle legacy format
285
+ messages = message_object
204
286
 
205
287
  if get_config().verbose:
206
- console.print(f"[bold green]✅ Loaded previous conversation from {messages_file}[/bold green]")
288
+ console.print(f"[bold green]✅ Loaded latest conversation from {latest_file}[/bold green]")
207
289
  console.print(f"[dim]📝 Conversation has {len(messages)} messages[/dim]")
208
290
 
209
291
  return messages
210
292
  except Exception as e:
211
- console.print(f"[bold red]❌ Error loading previous conversation:[/bold red] {str(e)}")
293
+ console.print(f"[bold red]❌ Error loading conversation:[/bold red] {str(e)}")
212
294
  return None
213
295
 
214
- def handle_query(query: str, temperature: float, verbose: bool, show_tokens: bool, continue_conversation: bool = False) -> None:
296
+ def handle_query(query: str, temperature: float, verbose: bool, show_tokens: bool, continue_conversation: Optional[str] = None) -> None:
215
297
  """
216
298
  Handle a query by initializing the agent and sending the query.
217
299
 
@@ -220,24 +302,29 @@ def handle_query(query: str, temperature: float, verbose: bool, show_tokens: boo
220
302
  temperature: Temperature value for model generation
221
303
  verbose: Whether to enable verbose mode
222
304
  show_tokens: Whether to show detailed token usage
223
- continue_conversation: Whether to continue the previous conversation
305
+ continue_conversation: Optional message ID to continue a specific conversation
224
306
  """
225
307
  # Initialize the agent
226
308
  agent = initialize_agent(temperature, verbose)
227
309
 
228
310
  # Load previous messages if continuing conversation
229
- if continue_conversation:
230
- messages = load_messages()
311
+ if continue_conversation is not None:
312
+ # If continue_conversation is an empty string (from flag with no value), use default behavior
313
+ message_id = None if continue_conversation == "" else continue_conversation
314
+ messages = load_messages(message_id)
231
315
  if messages:
232
316
  agent.set_messages(messages)
233
- console.print("[bold blue]🔄 Continuing previous conversation[/bold blue]")
317
+ if message_id:
318
+ console.print(f"[bold blue]🔄 Continuing conversation with ID: {message_id}[/bold blue]")
319
+ else:
320
+ console.print("[bold blue]🔄 Continuing previous conversation[/bold blue]")
234
321
 
235
322
  # Send the query to the agent
236
323
  try:
237
324
  agent.query(query)
238
325
 
239
- # Save messages after successful query
240
- save_messages(agent)
326
+ # Save messages after successful query and get the message ID
327
+ message_id = save_messages(agent)
241
328
 
242
329
  # Print token usage report
243
330
  if show_tokens:
@@ -249,13 +336,26 @@ def handle_query(query: str, temperature: float, verbose: bool, show_tokens: boo
249
336
  # Print tool usage statistics
250
337
  from janito.tools import print_usage_stats
251
338
  print_usage_stats()
339
+
340
+ # Show message about continuing this conversation
341
+ if message_id:
342
+ script_name = "janito"
343
+ try:
344
+ # Check if we're running from the entry point script or as a module
345
+ if sys.argv[0].endswith(('janito', 'janito.exe')):
346
+ console.print(f"[bold green]💬 This conversation can be continued with:[/bold green] {script_name} --continue {message_id} <request>")
347
+ else:
348
+ console.print(f"[bold green]💬 This conversation can be continued with:[/bold green] python -m janito --continue {message_id} <request>")
349
+ except:
350
+ # Fallback message
351
+ console.print(f"[bold green]💬 This conversation can be continued with:[/bold green] --continue {message_id} <request>")
252
352
 
253
353
  except KeyboardInterrupt:
254
354
  # Handle Ctrl+C by printing token and tool usage information
255
355
  console.print("\n[bold yellow]⚠️ Query interrupted by user (Ctrl+C)[/bold yellow]")
256
356
 
257
357
  # Save messages even if interrupted
258
- save_messages(agent)
358
+ message_id = save_messages(agent)
259
359
 
260
360
  # Print token usage report (even if interrupted)
261
361
  try:
@@ -268,6 +368,19 @@ def handle_query(query: str, temperature: float, verbose: bool, show_tokens: boo
268
368
  # Print tool usage statistics
269
369
  from janito.tools import print_usage_stats
270
370
  print_usage_stats()
371
+
372
+ # Show message about continuing this conversation
373
+ if message_id:
374
+ script_name = "janito"
375
+ try:
376
+ # Check if we're running from the entry point script or as a module
377
+ if sys.argv[0].endswith(('janito', 'janito.exe')):
378
+ console.print(f"[bold green]💬 This conversation can be continued with:[/bold green] {script_name} --continue {message_id} <request>")
379
+ else:
380
+ console.print(f"[bold green]💬 This conversation can be continued with:[/bold green] python -m janito --continue {message_id} <request>")
381
+ except:
382
+ # Fallback message
383
+ console.print(f"[bold green]💬 This conversation can be continued with:[/bold green] --continue {message_id} <request>")
271
384
  except Exception as e:
272
385
  console.print(f"[bold red]❌ Error generating usage report:[/bold red] {str(e)}")
273
386
  if verbose:
janito/cli/app.py CHANGED
@@ -19,18 +19,19 @@ console = Console()
19
19
  def main(ctx: typer.Context,
20
20
  query: Optional[str] = typer.Argument(None, help="Query to send to the claudine agent"),
21
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"),
22
+ show_tokens: bool = typer.Option(False, "--show-tokens", "--tokens", help="Show detailed token usage and pricing information"),
23
23
  workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Set the workspace directory"),
24
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
25
  show_config: bool = typer.Option(False, "--show-config", help="Show current configuration"),
26
26
  reset_config: bool = typer.Option(False, "--reset-config", help="Reset configuration by removing the config file"),
27
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
28
  ask: bool = typer.Option(False, "--ask", help="Enable ask mode which disables tools that perform changes"),
29
+ trust: bool = typer.Option(False, "--trust", "-t", help="Enable trust mode which suppresses tool outputs for a more concise execution (per-session setting)"),
29
30
  temperature: float = typer.Option(0.0, "--temperature", help="Set the temperature for model generation (0.0 to 1.0)"),
30
31
  profile: Optional[str] = typer.Option(None, "--profile", help="Use a predefined parameter profile (precise, balanced, conversational, creative, technical)"),
31
32
  role: Optional[str] = typer.Option(None, "--role", help="Set the assistant's role (default: 'software engineer')"),
32
33
  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
+ continue_conversation: Optional[str] = typer.Option(None, "--continue", "-c", help="Continue a previous conversation, optionally with a specific message ID")):
34
35
  """
35
36
  Janito CLI tool. If a query is provided without a command, it will be sent to the claudine agent.
36
37
  """
@@ -40,9 +41,16 @@ def main(ctx: typer.Context,
40
41
  # Set ask mode in config
41
42
  get_config().ask_mode = ask
42
43
 
44
+ # Set trust mode in config
45
+ get_config().trust_mode = trust
46
+
43
47
  # Show a message if ask mode is enabled
44
48
  if ask:
45
49
  console.print("[bold yellow]⚠️ Ask Mode enabled:[/bold yellow] 🔒 Tools that perform changes are disabled")
50
+
51
+ # Show a message if trust mode is enabled
52
+ if trust:
53
+ console.print("[bold blue]⚡ Trust Mode enabled:[/bold blue] Tool outputs are suppressed for concise execution (per-session setting)")
46
54
 
47
55
  # Show version and exit if requested
48
56
  if version:
janito/cli/commands.py CHANGED
@@ -285,7 +285,7 @@ def handle_config_commands(
285
285
  set_api_key: Optional[str],
286
286
  config_str: Optional[str],
287
287
  query: Optional[str],
288
- continue_conversation: bool = False
288
+ continue_conversation: Optional[str] = None
289
289
  ) -> bool:
290
290
  """
291
291
  Handle all configuration-related commands.
@@ -300,7 +300,7 @@ def handle_config_commands(
300
300
  set_api_key: API key
301
301
  config_str: Configuration string in format 'key=value'
302
302
  query: Query string
303
- continue_conversation: Whether to continue the previous conversation
303
+ continue_conversation: Optional message ID to continue a specific conversation
304
304
 
305
305
  Returns:
306
306
  bool: True if the program should exit after these operations
janito/config.py CHANGED
@@ -53,6 +53,7 @@ class Config:
53
53
  cls._instance._verbose = False
54
54
  # Chat history context feature has been removed
55
55
  cls._instance._ask_mode = False
56
+ cls._instance._trust_mode = False # New trust mode setting
56
57
  # Set technical profile as default
57
58
  profile_data = PROFILES["technical"]
58
59
  cls._instance._temperature = profile_data["temperature"]
@@ -74,6 +75,8 @@ class Config:
74
75
  self._verbose = config_data["debug_mode"]
75
76
  if "ask_mode" in config_data:
76
77
  self._ask_mode = config_data["ask_mode"]
78
+ if "trust_mode" in config_data:
79
+ self._trust_mode = config_data["trust_mode"]
77
80
  if "temperature" in config_data:
78
81
  self._temperature = config_data["temperature"]
79
82
  if "profile" in config_data:
@@ -95,6 +98,7 @@ class Config:
95
98
  # Chat history context feature has been removed
96
99
  "verbose": self._verbose,
97
100
  "ask_mode": self._ask_mode,
101
+ # trust_mode is not saved as it's a per-session setting
98
102
  "temperature": self._temperature,
99
103
  "role": self._role
100
104
  }
@@ -265,6 +269,21 @@ class Config:
265
269
  self._ask_mode = value
266
270
  self._save_config()
267
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
+
268
287
  @property
269
288
  def temperature(self) -> float:
270
289
  """Get the temperature value for model generation."""
@@ -323,6 +342,7 @@ class Config:
323
342
  self._verbose = False
324
343
  # Chat history context feature has been removed
325
344
  self._ask_mode = False
345
+ self._trust_mode = False
326
346
  # Set technical profile as default
327
347
  profile_data = PROFILES["technical"]
328
348
  self._temperature = profile_data["temperature"]
@@ -6,22 +6,23 @@ If the question is related to the project, use the tools using the relative path
6
6
  If creating or editing files with a large number of lines, organize them into smaller files.
7
7
  If creating or editing files in an existing directory check surrounding files for the used patterns.
8
8
 
9
- # Structure Discovery (.janito/docs/STRUCTURE.md)
10
- Always start exploring the project by viewing for the file .janito/docs/STRUCTURE.md.
9
+ # Structure Discovery (./docs/STRUCTURE.md)
10
+ Always start exploring the project by viewing for the file ./docs/STRUCTURE.md.
11
11
  Do not track files or directories wich are in .gitignore in the structure.
12
12
  At the end of responding to the user, update the structure file based on the files and directories you have interacted with,
13
13
  be precise focusing on the most important files and directories, avoid adding extra information like architecture or design patterns.
14
14
 
15
-
16
15
  # Tools
17
16
  The bash tool does not support commands which will require user input.
18
17
  Prefer the str_replace_editor tool to view directories and file contents.
19
18
 
20
19
  </IMPORTANT>
21
- Call the tool user_prompt when:
20
+ Call the user_prompt tool when:
22
21
  - There are multiple options to apply a certain change
23
22
  - The next operation risk is moderated or high
24
23
  - The implementation plan is complex, requiring a review
25
24
  Proceed according to the user answer.
26
25
  <IMPORTANT/>
27
26
 
27
+ When changing code in Python files, be mindful about the need to review the imports specially when new type hints are used (eg. Optional, Tuple, List, Dict, etc).
28
+ After performing changes to a project in interfaces which are exposed to the user, provide a short summary on how to verify the changes. eg. "run cmd xpto"
janito/tools/bash/bash.py CHANGED
@@ -34,7 +34,9 @@ def bash_tool(command: str, restart: Optional[bool] = False) -> Tuple[str, bool]
34
34
  # Import console for printing output in real-time
35
35
  from janito.tools.rich_console import console, print_info
36
36
 
37
- print_info(f"{command}", "Bash Run")
37
+ # Only print command if not in trust mode
38
+ if not get_config().trust_mode:
39
+ print_info(f"{command}", "Bash Run")
38
40
  global _bash_session
39
41
 
40
42
  # Check if in ask mode and if the command might modify files
@@ -1,182 +1,184 @@
1
- import subprocess
2
- import time
3
- import uuid
4
-
5
- class PersistentBash:
6
- """
7
- A wrapper class that maintains a persistent Bash session.
8
- Allows sending commands and collecting output without restarting Bash.
9
- """
10
-
11
- def __init__(self, bash_path=None):
12
- """
13
- Initialize a persistent Bash session.
14
-
15
- Args:
16
- bash_path (str, optional): Path to the Bash executable. If None, tries to detect automatically.
17
- """
18
- self.process = None
19
- self.bash_path = bash_path
20
-
21
- # If bash_path is not provided, try to detect it
22
- if self.bash_path is None:
23
- # On Unix-like systems, bash is usually in the PATH
24
- self.bash_path = "bash"
25
-
26
- # Check if bash exists
27
- try:
28
- subprocess.run(["which", "bash"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
29
- except subprocess.CalledProcessError as err:
30
- raise FileNotFoundError("Could not find bash executable. Please specify the path manually.") from err
31
-
32
- # Start the bash process
33
- self.start_process()
34
-
35
- def start_process(self):
36
- """Start the Bash process."""
37
- # Create a subprocess with pipe for stdin, stdout, and stderr
38
- bash_args = [self.bash_path]
39
-
40
- self.process = subprocess.Popen(
41
- bash_args,
42
- stdin=subprocess.PIPE,
43
- stdout=subprocess.PIPE,
44
- stderr=subprocess.STDOUT, # Redirect stderr to stdout
45
- text=True, # Use text mode for input/output
46
- bufsize=0, # Unbuffered
47
- universal_newlines=True, # Universal newlines mode
48
- )
49
-
50
- # Set up a more reliable environment
51
- setup_commands = [
52
- "export PS1='$ '", # Simple prompt to avoid parsing issues
53
- "export TERM=dumb", # Disable color codes and other terminal features
54
- "set +o history", # Disable history
55
- "shopt -s expand_aliases", # Enable alias expansion
56
- ]
57
-
58
- # Send setup commands
59
- for cmd in setup_commands:
60
- self._send_command(cmd)
61
-
62
- # Clear initial output with a marker
63
- marker = f"INIT_COMPLETE_{uuid.uuid4().hex}"
64
- self._send_command(f"echo {marker}")
65
-
66
- while True:
67
- line = self.process.stdout.readline().strip()
68
- if marker in line:
69
- break
70
-
71
- def _send_command(self, command):
72
- """Send a command to the Bash process without reading the output."""
73
- if self.process is None or self.process.poll() is not None:
74
- self.start_process()
75
-
76
- self.process.stdin.write(command + "\n")
77
- self.process.stdin.flush()
78
-
79
- def execute(self, command, timeout=None):
80
- """
81
- Execute a command in the Bash session and return the output.
82
-
83
- Args:
84
- command (str): The command to execute.
85
- timeout (int, optional): Timeout in seconds. If None, no timeout is applied.
86
-
87
- Returns:
88
- str: The command output.
89
- """
90
- from janito.tools.rich_console import console
91
-
92
- if self.process is None or self.process.poll() is not None:
93
- # Process has terminated, restart it
94
- self.start_process()
95
-
96
- # Create a unique marker to identify the end of output
97
- end_marker = f"END_OF_COMMAND_{uuid.uuid4().hex}"
98
-
99
- # Construct the wrapped command with echo markers
100
- # Only use timeout when explicitly requested
101
- if timeout is not None and timeout > 0:
102
- # Check if timeout command is available
103
- is_timeout_available = False
104
- try:
105
- check_cmd = "command -v timeout > /dev/null 2>&1 && echo available || echo unavailable"
106
- self._send_command(check_cmd)
107
- for _ in range(10): # Read up to 10 lines to find the result
108
- line = self.process.stdout.readline().strip()
109
- if "available" in line:
110
- is_timeout_available = True
111
- break
112
- elif "unavailable" in line:
113
- is_timeout_available = False
114
- break
115
- except:
116
- is_timeout_available = False
117
-
118
- if is_timeout_available:
119
- # For timeout to work with shell syntax, we need to use bash -c
120
- escaped_command = command.replace('"', '\\"')
121
- wrapped_command = f"timeout {timeout}s bash -c \"{escaped_command}\" 2>&1; echo '{end_marker}'"
122
- else:
123
- wrapped_command = f"{command} 2>&1; echo '{end_marker}'"
124
- else:
125
- wrapped_command = f"{command} 2>&1; echo '{end_marker}'"
126
-
127
- # Send the command
128
- self._send_command(wrapped_command)
129
-
130
- # Collect output until the end marker is found
131
- output_lines = []
132
- start_time = time.time()
133
- max_wait = timeout if timeout is not None else 3600 # Default to 1 hour if no timeout
134
-
135
- while time.time() - start_time < max_wait + 5: # Add buffer time
136
- try:
137
- line = self.process.stdout.readline().rstrip('\r\n')
138
- if end_marker in line:
139
- break
140
-
141
- # Print the output to the console in real-time
142
- if line:
143
- console.print(line)
144
-
145
- output_lines.append(line)
146
- except Exception as e:
147
- error_msg = f"[Error reading output: {str(e)}]"
148
- console.print(error_msg, style="red")
149
- output_lines.append(error_msg)
150
- continue
151
-
152
- # Check for timeout
153
- if time.time() - start_time >= max_wait + 5:
154
- timeout_msg = f"Error: Command timed out after {max_wait} seconds"
155
- console.print(timeout_msg, style="red bold")
156
- output_lines.append(timeout_msg)
157
-
158
- # Try to reset the bash session after a timeout
159
- self.close()
160
- self.start_process()
161
-
162
- return "\n".join(output_lines)
163
-
164
- def close(self):
165
- """Close the Bash session."""
166
- if self.process and self.process.poll() is None:
167
- try:
168
- self._send_command("exit")
169
- self.process.wait(timeout=2)
170
- except:
171
- pass
172
- finally:
173
- try:
174
- self.process.terminate()
175
- except:
176
- pass
177
-
178
- self.process = None
179
-
180
- def __del__(self):
181
- """Destructor to ensure the process is closed."""
1
+ import subprocess
2
+ import time
3
+ import uuid
4
+
5
+ class PersistentBash:
6
+ """
7
+ A wrapper class that maintains a persistent Bash session.
8
+ Allows sending commands and collecting output without restarting Bash.
9
+ """
10
+
11
+ def __init__(self, bash_path=None):
12
+ """
13
+ Initialize a persistent Bash session.
14
+
15
+ Args:
16
+ bash_path (str, optional): Path to the Bash executable. If None, tries to detect automatically.
17
+ """
18
+ self.process = None
19
+ self.bash_path = bash_path
20
+
21
+ # If bash_path is not provided, try to detect it
22
+ if self.bash_path is None:
23
+ # On Unix-like systems, bash is usually in the PATH
24
+ self.bash_path = "bash"
25
+
26
+ # Check if bash exists
27
+ try:
28
+ subprocess.run(["which", "bash"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
29
+ except subprocess.CalledProcessError as err:
30
+ raise FileNotFoundError("Could not find bash executable. Please specify the path manually.") from err
31
+
32
+ # Start the bash process
33
+ self.start_process()
34
+
35
+ def start_process(self):
36
+ """Start the Bash process."""
37
+ # Create a subprocess with pipe for stdin, stdout, and stderr
38
+ bash_args = [self.bash_path]
39
+
40
+ self.process = subprocess.Popen(
41
+ bash_args,
42
+ stdin=subprocess.PIPE,
43
+ stdout=subprocess.PIPE,
44
+ stderr=subprocess.STDOUT, # Redirect stderr to stdout
45
+ text=True, # Use text mode for input/output
46
+ bufsize=0, # Unbuffered
47
+ universal_newlines=True, # Universal newlines mode
48
+ )
49
+
50
+ # Set up a more reliable environment
51
+ setup_commands = [
52
+ "export PS1='$ '", # Simple prompt to avoid parsing issues
53
+ "export TERM=dumb", # Disable color codes and other terminal features
54
+ "set +o history", # Disable history
55
+ "shopt -s expand_aliases", # Enable alias expansion
56
+ ]
57
+
58
+ # Send setup commands
59
+ for cmd in setup_commands:
60
+ self._send_command(cmd)
61
+
62
+ # Clear initial output with a marker
63
+ marker = f"INIT_COMPLETE_{uuid.uuid4().hex}"
64
+ self._send_command(f"echo {marker}")
65
+
66
+ while True:
67
+ line = self.process.stdout.readline().strip()
68
+ if marker in line:
69
+ break
70
+
71
+ def _send_command(self, command):
72
+ """Send a command to the Bash process without reading the output."""
73
+ if self.process is None or self.process.poll() is not None:
74
+ self.start_process()
75
+
76
+ self.process.stdin.write(command + "\n")
77
+ self.process.stdin.flush()
78
+
79
+ def execute(self, command, timeout=None):
80
+ """
81
+ Execute a command in the Bash session and return the output.
82
+
83
+ Args:
84
+ command (str): The command to execute.
85
+ timeout (int, optional): Timeout in seconds. If None, no timeout is applied.
86
+
87
+ Returns:
88
+ str: The command output.
89
+ """
90
+ from janito.tools.rich_console import console
91
+
92
+ if self.process is None or self.process.poll() is not None:
93
+ # Process has terminated, restart it
94
+ self.start_process()
95
+
96
+ # Create a unique marker to identify the end of output
97
+ end_marker = f"END_OF_COMMAND_{uuid.uuid4().hex}"
98
+
99
+ # Construct the wrapped command with echo markers
100
+ # Only use timeout when explicitly requested
101
+ if timeout is not None and timeout > 0:
102
+ # Check if timeout command is available
103
+ is_timeout_available = False
104
+ try:
105
+ check_cmd = "command -v timeout > /dev/null 2>&1 && echo available || echo unavailable"
106
+ self._send_command(check_cmd)
107
+ for _ in range(10): # Read up to 10 lines to find the result
108
+ line = self.process.stdout.readline().strip()
109
+ if "available" in line:
110
+ is_timeout_available = True
111
+ break
112
+ elif "unavailable" in line:
113
+ is_timeout_available = False
114
+ break
115
+ except:
116
+ is_timeout_available = False
117
+
118
+ if is_timeout_available:
119
+ # For timeout to work with shell syntax, we need to use bash -c
120
+ escaped_command = command.replace('"', '\\"')
121
+ wrapped_command = f"timeout {timeout}s bash -c \"{escaped_command}\" 2>&1; echo '{end_marker}'"
122
+ else:
123
+ wrapped_command = f"{command} 2>&1; echo '{end_marker}'"
124
+ else:
125
+ wrapped_command = f"{command} 2>&1; echo '{end_marker}'"
126
+
127
+ # Send the command
128
+ self._send_command(wrapped_command)
129
+
130
+ # Collect output until the end marker is found
131
+ output_lines = []
132
+ start_time = time.time()
133
+ max_wait = timeout if timeout is not None else 3600 # Default to 1 hour if no timeout
134
+
135
+ while time.time() - start_time < max_wait + 5: # Add buffer time
136
+ try:
137
+ line = self.process.stdout.readline().rstrip('\r\n')
138
+ if end_marker in line:
139
+ break
140
+
141
+ # Print the output to the console in real-time if not in trust mode
142
+ if line:
143
+ from janito.config import get_config
144
+ if not get_config().trust_mode:
145
+ console.print(line)
146
+
147
+ output_lines.append(line)
148
+ except Exception as e:
149
+ error_msg = f"[Error reading output: {str(e)}]"
150
+ console.print(error_msg, style="red")
151
+ output_lines.append(error_msg)
152
+ continue
153
+
154
+ # Check for timeout
155
+ if time.time() - start_time >= max_wait + 5:
156
+ timeout_msg = f"Error: Command timed out after {max_wait} seconds"
157
+ console.print(timeout_msg, style="red bold")
158
+ output_lines.append(timeout_msg)
159
+
160
+ # Try to reset the bash session after a timeout
161
+ self.close()
162
+ self.start_process()
163
+
164
+ return "\n".join(output_lines)
165
+
166
+ def close(self):
167
+ """Close the Bash session."""
168
+ if self.process and self.process.poll() is None:
169
+ try:
170
+ self._send_command("exit")
171
+ self.process.wait(timeout=2)
172
+ except:
173
+ pass
174
+ finally:
175
+ try:
176
+ self.process.terminate()
177
+ except:
178
+ pass
179
+
180
+ self.process = None
181
+
182
+ def __del__(self):
183
+ """Destructor to ensure the process is closed."""
182
184
  self.close()
@@ -222,9 +222,11 @@ class PersistentBash:
222
222
  if end_marker in line:
223
223
  break
224
224
 
225
- # Print the output to the console in real-time
225
+ # Print the output to the console in real-time if not in trust mode
226
226
  if line:
227
- console.print(line)
227
+ from janito.config import get_config
228
+ if not get_config().trust_mode:
229
+ console.print(line)
228
230
 
229
231
  output_lines.append(line)
230
232
  except UnicodeDecodeError as e:
@@ -4,6 +4,7 @@ Utility module for rich console printing in tools.
4
4
  from rich.console import Console
5
5
  from rich.text import Text
6
6
  from typing import Optional
7
+ from janito.config import get_config
7
8
 
8
9
  # Create a shared console instance
9
10
  console = Console()
@@ -16,6 +17,9 @@ def print_info(message: str, title: Optional[str] = None):
16
17
  message: The message to print
17
18
  title: Optional title for the panel
18
19
  """
20
+ # Skip printing if trust mode is enabled
21
+ if get_config().trust_mode:
22
+ return
19
23
  # Map titles to specific icons
20
24
  icon_map = {
21
25
  # File operations
@@ -82,20 +86,22 @@ def print_info(message: str, title: Optional[str] = None):
82
86
  elif "Undoing last edit" in title:
83
87
  icon = "↩️" # Undo icon
84
88
 
89
+ # Add indentation to all tool messages
90
+ indent = " "
85
91
  text = Text(message)
86
92
  if title:
87
93
  # Special case for Bash Run commands
88
94
  if title == "Bash Run":
89
95
  console.print("\n" + "-"*50)
90
- console.print(f"{icon} {title}", style="bold white on blue")
96
+ console.print(f"{indent}{icon} {title}", style="bold white on blue")
91
97
  console.print("-"*50)
92
- console.print(f"$ {text}", style="white on dark_blue")
98
+ console.print(f"{indent}$ {text}", style="white on dark_blue")
93
99
  # Make sure we're not returning anything
94
100
  return
95
101
  else:
96
- console.print(f"{icon} {message}", style="blue", end="")
102
+ console.print(f"{indent}{icon} {message}", style="blue", end="")
97
103
  else:
98
- console.print(f"{icon} {text}", style="blue", end="")
104
+ console.print(f"{indent}{icon} {text}", style="blue", end="")
99
105
 
100
106
  def print_success(message: str, title: Optional[str] = None):
101
107
  """
@@ -105,6 +111,9 @@ def print_success(message: str, title: Optional[str] = None):
105
111
  message: The message to print
106
112
  title: Optional title for the panel
107
113
  """
114
+ # Skip printing if trust mode is enabled
115
+ if get_config().trust_mode:
116
+ return
108
117
  text = Text(message)
109
118
  if title:
110
119
  console.print(f" ✅ {message}", style="green")
@@ -114,26 +123,54 @@ def print_success(message: str, title: Optional[str] = None):
114
123
  def print_error(message: str, title: Optional[str] = None):
115
124
  """
116
125
  Print an error message with rich formatting.
126
+ In trust mode, error messages are suppressed.
117
127
 
118
128
  Args:
119
129
  message: The message to print
120
130
  title: Optional title for the panel
121
131
  """
132
+ # Skip printing if trust mode is enabled
133
+ if get_config().trust_mode:
134
+ return
135
+
122
136
  text = Text(message)
123
- if title:
124
- # Special case for File View - print without header
137
+
138
+ # Check if message starts with question mark emoji (❓)
139
+ # If it does, use warning styling (yellow) instead of error styling (red)
140
+ starts_with_question_mark = message.startswith("❓")
141
+
142
+ if starts_with_question_mark:
143
+ # Use warning styling for question mark emoji errors
144
+ # For question mark emoji errors, don't include the title (like "Error")
145
+ # Just print the message with the emoji
125
146
  if title == "File View":
126
- console.print(f"\n {message}", style="red")
147
+ console.print(f"\n {message}", style="yellow")
127
148
  else:
128
- console.print(f"{title} {text}")
149
+ console.print(f"{message}", style="yellow")
129
150
  else:
130
- console.print(f"\n❌ {text}", style="red")
151
+ # Regular error styling
152
+ if title:
153
+ # Special case for File View - print without header
154
+ if title == "File View":
155
+ console.print(f"\n ❌ {message}", style="red")
156
+ # Special case for Search Error
157
+ elif title == "Search Error":
158
+ console.print(f"❌ {message}", style="red")
159
+ else:
160
+ console.print(f"❌ {title} {text}", style="red")
161
+ else:
162
+ console.print(f"\n❌ {text}", style="red")
131
163
 
132
164
  def print_warning(message: str):
133
165
  """
134
166
  Print a warning message with rich formatting.
167
+ In trust mode, warning messages are suppressed.
135
168
 
136
169
  Args:
137
170
  message: The message to print
138
171
  """
172
+ # Skip printing if trust mode is enabled
173
+ if get_config().trust_mode:
174
+ return
175
+
139
176
  console.print(f"⚠️ {message}", style="yellow")
@@ -37,9 +37,10 @@ def search_text(text_pattern: str, file_pattern: str = "*", root_dir: str = ".",
37
37
  # Compile the regex pattern for better performance
38
38
  try:
39
39
  regex = re.compile(text_pattern)
40
- except re.error as e:
41
- error_msg = f"Error: Invalid regex pattern '{text_pattern}': {str(e)}"
42
- print_error(error_msg, "Regex Error")
40
+ except re.error:
41
+ # Simplified error message without the specific regex error details
42
+ error_msg = f"Error: Invalid regex pattern '{text_pattern}'"
43
+ print_error(error_msg, "Search Error")
43
44
  return error_msg, True
44
45
 
45
46
  matching_files = []
@@ -60,7 +60,9 @@ def handle_str_replace(args: Dict[str, Any]) -> Tuple[str, bool]:
60
60
 
61
61
  # Check if old_str exists in the content (must match EXACTLY)
62
62
  if old_str not in content:
63
- print_error("No exact match found for replacement. Please check your text and ensure whitespaces match exactly.", "Error")
63
+ # Only print error if not in trust mode
64
+ if not get_config().trust_mode:
65
+ print_error("No exact match", "?")
64
66
  return ("Error: No exact match found for replacement. Please check your text and ensure whitespaces match exactly.", True)
65
67
 
66
68
  # Count occurrences to check for multiple matches
@@ -55,8 +55,8 @@ def handle_view(args: Dict[str, Any]) -> Tuple[str, bool]:
55
55
  file_path = pathlib.Path(path)
56
56
 
57
57
  if not file_path.exists():
58
- print_error(f"File or directory {path} does not exist", "Error")
59
- return (f"File or directory {path} does not exist", True)
58
+ print_error(f" (not found)", "Error")
59
+ return (f" (not found)", True)
60
60
 
61
61
  # If the path is a directory, list non-hidden files and directories up to 2 levels deep
62
62
  if file_path.is_dir():
@@ -94,9 +94,12 @@ def handle_view(args: Dict[str, Any]) -> Tuple[str, bool]:
94
94
  # Directory listings should not be truncated
95
95
  file_dir_count = len(result)
96
96
  output = "\n".join(result)
97
- console.print(f"Found ", style="default", end="")
98
- console.print(f"{file_dir_count}", style="cyan", end="")
99
- console.print(" files and directories")
97
+
98
+ # Only print count if not in trust mode
99
+ if not get_config().trust_mode:
100
+ console.print(f"Found ", style="default", end="")
101
+ console.print(f"{file_dir_count}", style="cyan", end="")
102
+ console.print(" files and directories")
100
103
  return (output, False)
101
104
  except Exception as e:
102
105
  return (f"Error listing directory {path}: {str(e)}", True)
@@ -144,9 +147,12 @@ def handle_view(args: Dict[str, Any]) -> Tuple[str, bool]:
144
147
  return (truncated_content + "\n<response clipped>", False)
145
148
 
146
149
  content_to_print = "".join(numbered_content)
147
- console.print("(", style="default", end="")
148
- console.print(f"{len(numbered_content)}", style="cyan", end="")
149
- console.print(")")
150
+
151
+ # Only print line count if not in trust mode
152
+ if not get_config().trust_mode:
153
+ console.print("(", style="default", end="")
154
+ console.print(f"{len(numbered_content)}", style="cyan", end="")
155
+ console.print(")")
150
156
  # Return the content as a string without any Rich objects
151
157
  return (content_to_print, False)
152
158
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janito
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: Janito CLI tool
5
5
  Project-URL: Homepage, https://github.com/joaompinto/janito
6
6
  Author-email: João Pinto <lamego.pinto@gmail.com>
@@ -35,6 +35,8 @@ Janito is a powerful AI-assisted command-line interface (CLI) tool built with Py
35
35
  - 🌐 Web page fetching with content extraction capabilities
36
36
  - 🔄 Parameter profiles for optimizing Claude's behavior for different tasks
37
37
  - 📋 Line delta tracking to monitor net changes in files
38
+ - 💬 Conversation history with ability to resume previous conversations
39
+ - 🔇 Trust mode for concise output without tool details
38
40
 
39
41
  ## 🛠️ System Requirements
40
42
 
@@ -108,14 +110,24 @@ janito --show-tokens "Explain what is in the project"
108
110
  # Use a specific parameter profile for creative tasks
109
111
  janito --profile creative "Write a fun description for our project"
110
112
 
111
- # Continue previous conversation
112
- janito --continue "Plese add one more line"
113
+ # Use trust mode for concise output without tool details
114
+ janito --trust "Optimize the HTML code"
115
+ # Or use the short alias
116
+ janito -t "Optimize the HTML code"
117
+
118
+ # Continue the most recent conversation
119
+ janito --continue "Please add one more line"
120
+
121
+ # Continue a specific conversation using its message ID
122
+ # (Janito displays the message ID after each conversation)
123
+ janito --continue abc123def "Let's refine that code"
113
124
 
114
125
  # Show current configuration and available profiles
115
126
  janito --show-config
116
127
 
117
128
  # You can press Ctrl+C at any time to interrupt a query
118
129
  # Janito will still display token and tool usage information
130
+ # Even interrupted conversations can be continued with --continue
119
131
  ```
120
132
 
121
133
  ## 🔧 Available Tools
@@ -134,7 +146,7 @@ Janito comes with several built-in tools:
134
146
  Janito includes a comprehensive token usage tracking system that helps you monitor API costs:
135
147
 
136
148
  - **Basic tracking**: By default, Janito displays a summary of token usage and cost after each query
137
- - **Detailed reporting**: Use the `--show-tokens` or `-t` flag to see detailed breakdowns including:
149
+ - **Detailed reporting**: Use the `--show-tokens` flag to see detailed breakdowns including:
138
150
  - Input and output token counts
139
151
  - Per-tool token usage statistics
140
152
  - Precise cost calculations
@@ -147,6 +159,11 @@ janito --show-tokens "Write a Python function to sort a list"
147
159
 
148
160
  # Basic usage (shows simplified token usage summary)
149
161
  janito "Explain Docker containers"
162
+
163
+ # Use trust mode for concise output without tool details
164
+ janito --trust "Create a simple Python script"
165
+ # Or use the short alias
166
+ janito -t "Create a simple Python script"
150
167
  ```
151
168
 
152
169
  The usage tracker automatically calculates cache savings, showing you how much you're saving by reusing previous responses.
@@ -169,6 +186,64 @@ janito --profile creative "Write a poem about coding"
169
186
  janito --show-config
170
187
  ```
171
188
 
189
+ ## 🔇 Trust Mode
190
+
191
+ Janito offers a trust mode that suppresses tool outputs for a more concise execution experience:
192
+
193
+ ### How It Works
194
+
195
+ - When enabled with `--trust` or `-t`, Janito suppresses informational and success messages from tools
196
+ - Only essential output and error messages are displayed
197
+ - The final result from Claude is still shown in full
198
+ - Trust mode is a per-session setting and not saved to your configuration
199
+
200
+ ### Using Trust Mode
201
+
202
+ ```bash
203
+ # Enable trust mode with the full flag
204
+ janito --trust "Create a Python script that reads a CSV file"
205
+
206
+ # Or use the short alias
207
+ janito -t "Create a Python script that reads a CSV file"
208
+ ```
209
+
210
+ This feature is particularly useful for:
211
+ - Experienced users who don't need to see every step of the process
212
+ - Batch processing or scripting where concise output is preferred
213
+ - Focusing on results rather than the process
214
+ - Creating cleaner output for documentation or sharing
215
+
216
+ ## 💬 Conversation History
217
+
218
+ Janito automatically saves your conversation history, allowing you to continue previous discussions:
219
+
220
+ ### How It Works
221
+
222
+ - Each conversation is saved with a unique message ID in `.janito/last_messages/`
223
+ - The most recent conversation is also saved as `.janito/last_message.json` for backward compatibility
224
+ - After each conversation, Janito displays the command to continue that specific conversation
225
+
226
+ ### Using the Continue Feature
227
+
228
+ ```bash
229
+ # Continue the most recent conversation
230
+ janito --continue "Add more details to your previous response"
231
+
232
+ # Continue a specific conversation using its ID
233
+ janito --continue abc123def "Let's modify that code you suggested"
234
+ ```
235
+
236
+ The `--continue` flag (or `-c` for short) allows you to:
237
+ - Resume the most recent conversation when used without an ID
238
+ - Resume a specific conversation when provided with a message ID
239
+ - Maintain context across multiple interactions for complex tasks
240
+
241
+ This feature is particularly useful for:
242
+ - Multi-step development tasks
243
+ - Iterative code improvements
244
+ - Continuing discussions after system interruptions
245
+ - Maintaining context when working on complex problems
246
+
172
247
  ## ⚙️ Dependencies
173
248
 
174
249
  Janito automatically installs the following dependencies:
@@ -177,6 +252,28 @@ Janito automatically installs the following dependencies:
177
252
  - claudine - For Claude AI integration
178
253
  - Additional packages for file handling and web content extraction
179
254
 
255
+ ## 🛠️ Command-Line Options
256
+
257
+ Janito offers a variety of command-line options to customize its behavior:
258
+
259
+ ```
260
+ --verbose, -v Enable verbose mode with detailed output
261
+ --show-tokens Show detailed token usage and pricing information
262
+ --workspace, -w TEXT Set the workspace directory
263
+ --set-config TEXT Configuration string in format 'key=value', e.g., 'temperature=0.7'
264
+ --show-config Show current configuration
265
+ --reset-config Reset configuration by removing the config file
266
+ --set-api-key TEXT Set the Anthropic API key globally in the user's home directory
267
+ --ask Enable ask mode which disables tools that perform changes
268
+ --trust, -t Enable trust mode which suppresses tool outputs for concise execution
269
+ --temperature FLOAT Set the temperature for model generation (0.0 to 1.0)
270
+ --profile TEXT Use a predefined parameter profile (precise, balanced, conversational, creative, technical)
271
+ --role TEXT Set the assistant's role (default: 'software engineer')
272
+ --version Show the version and exit
273
+ --continue, -c TEXT Continue a previous conversation, optionally with a specific message ID
274
+ --help Show the help message and exit
275
+ ```
276
+
180
277
  ## 🔑 API Key Configuration
181
278
 
182
279
  You can configure your Anthropic API key in several ways:
@@ -1,16 +1,16 @@
1
- janito/__init__.py,sha256=UhMcaPInS2sBvYxOLldeLu7sKy-ZZxndtrf6NPqQBgI,53
1
+ janito/__init__.py,sha256=5Cnu7sPJURDSY1cnxtGvzs0MpwHUsh5zYZRNQ2LkM8k,53
2
2
  janito/__main__.py,sha256=Oy-Nc1tZkpyvTKuq1R8oHSuJTkvptN6H93kIHBu7DKY,107
3
3
  janito/callbacks.py,sha256=E1FPXYHZUgiEGMabYuf999PSf_Su4ByHOWlc1-hMqWE,915
4
- janito/config.py,sha256=haIErp8tVL6e_5h4fsgLqFib2aCAZZ3sIXa8wStn_yo,12424
4
+ janito/config.py,sha256=MHfloii_OnOVcV4pbunfdnAv8uJzxu_ytDK34Rj8yZ8,13221
5
5
  janito/test_file.py,sha256=c6GWGdTYG3z-Y5XBao9Tmhmq3G-v0L37OfwLgBo8zIU,126
6
6
  janito/token_report.py,sha256=Mks7o2yTxPChgQyBJNoQ5eMmrhSgEM4LKCKi2tHJbVo,9580
7
7
  janito/cli/__init__.py,sha256=dVi9l3E86YyukjxQ-XSUnMZkghnNasXex-X5XAOBiwk,85
8
- janito/cli/agent.py,sha256=AYJhSqPbz1Ywz_RBzHxfXIK6x-aZrqA8-C2QzmxZ3J8,10533
9
- janito/cli/app.py,sha256=RghsHSmvnECGWzoAeQOJdxHTZM_Xcw6Y4jgbJ5I6-5M,4131
10
- janito/cli/commands.py,sha256=c1EHJ_VZR_3aA3zRvCLaBn9yfdj5emQpTI0Z_bXcIyc,12664
8
+ janito/cli/agent.py,sha256=iFwzbZupkpXQVO0YrQmCGKKQGcqCRdAY3JLg6tZaJiQ,15940
9
+ janito/cli/app.py,sha256=czgOEWCsWlKeLWMHxPv6fxOStoL2wcC8_QZs6mMtTp8,4643
10
+ janito/cli/commands.py,sha256=pgassnKENr0DGcZT3tFXtz8MCVuEZwHgxiBktgAik6g,12682
11
11
  janito/cli/output.py,sha256=mo3hUokhrD4SWexUjCbLGGQeCDUf0369DA_i9BW7HjU,933
12
12
  janito/cli/utils.py,sha256=gO4NtCNwtEzYDsQesrFlqB5FtYuw87yGwo4iG3nINgw,661
13
- janito/data/instructions_template.txt,sha256=oXmjVEajwLuTjcyXyD123CGSjNG1QLbfzbw9iEq-w5w,1411
13
+ janito/data/instructions_template.txt,sha256=FlyGpNjl2LRBjPWLTK40oKOSH6sveHp5ud35nf_lfs8,1716
14
14
  janito/tools/__init__.py,sha256=hio75FRkxLSQB13We-SCCM7Qa9lMqWCfWhEonHlTr8M,1405
15
15
  janito/tools/decorators.py,sha256=Tp48n5y4LKsjyV3HeOA9wk2dV413RrEG-23kRyQVlKs,2522
16
16
  janito/tools/delete_file.py,sha256=UrZ5q59SIxWfuJcqgol6yPBqL-RhO9lFCF4MqAc6o00,2252
@@ -18,12 +18,12 @@ janito/tools/find_files.py,sha256=c_N9ETcRPprQeuZYanwFnl-9E05ZqUYhNVoCRS5uqQg,83
18
18
  janito/tools/move_file.py,sha256=FCs1ghalfHlXmcbAA_IlLcUll9hTOU1MMFGrTWopXvM,2741
19
19
  janito/tools/prompt_user.py,sha256=OnTiWVBCbL_2MYu7oThlKr8X_pnYdG-dzxXSOgJF41c,1942
20
20
  janito/tools/replace_file.py,sha256=i4GoLtS14eKSU5lYI18mJ96S0_ekeHMwlQfazg-fxrM,2296
21
- janito/tools/rich_console.py,sha256=VyLStA7fCgMGp9KZ0TYHAC3FOOAWKKHx1XTWWWF2ISI,4560
22
- janito/tools/search_text.py,sha256=hZLgKlF_cC5JRQ2XsussndSZMmptwr8d0tTKCyCXFwg,9755
21
+ janito/tools/rich_console.py,sha256=0zWYRF8qk4N-upuwswUSEfYFAfkEYDYeCgxst-czWtY,6044
22
+ janito/tools/search_text.py,sha256=RxbnNeZ3ErCvevwbl60fQAe55QYtU-D9n13iixeDcqQ,9822
23
23
  janito/tools/usage_tracker.py,sha256=IE7GVBDYsX2EDLjsVaVqTeAnT2SAYfcOJvBaH__wHdU,4613
24
- janito/tools/bash/bash.py,sha256=gOYhp2CexuEjSSpHGhVQLr-Ooc67Etl7Br1TbRtY6vc,3577
25
- janito/tools/bash/unix_persistent_bash.py,sha256=tFFhbG9rk1KcONmG0j6SZHrqYxCXELhzobRSTEHPFm0,6868
26
- janito/tools/bash/win_persistent_bash.py,sha256=mMgAdRi7jlznq7ssNmgiSMEqzEI-rIV0kgYpndcum6U,12758
24
+ janito/tools/bash/bash.py,sha256=AFm0w_z-yyYRWxuR744OFpm5iCZaZpE-pWbnKbgajp4,3665
25
+ janito/tools/bash/unix_persistent_bash.py,sha256=I59PPQiXHscPJ6Y7ev_83dLFNFWq1hKwAK9kFXdnbBY,7185
26
+ janito/tools/bash/win_persistent_bash.py,sha256=96xm_yijjc6hBYfNluLahbvR2oUuHug_JkoMah7Hy38,12894
27
27
  janito/tools/fetch_webpage/__init__.py,sha256=0RG0ev-yrfo8gPzt-36WMpVdY2qtMhk2Z-mVpq-7m1o,1076
28
28
  janito/tools/fetch_webpage/chunking.py,sha256=mMtrZeirZ_GnKOOzeG8BijLVpSUI0-TA5Ioqi2HKb1A,2971
29
29
  janito/tools/fetch_webpage/core.py,sha256=3XDvYnC_UbQUNumwWw32hfJhjJUEaPzzoF2yhxhwxos,7764
@@ -36,12 +36,12 @@ janito/tools/str_replace_editor/utils.py,sha256=akiPqCHjky_RwL9OitHJJ7uQ-3fNaA8w
36
36
  janito/tools/str_replace_editor/handlers/__init__.py,sha256=RP6JCeDRIL4R-lTpGowIoOAi64gg6VxZvJGp8Q2UOVU,373
37
37
  janito/tools/str_replace_editor/handlers/create.py,sha256=s8RQE04kDAL7OLZA8WxJkDqTmJlGmCNiit4tIHnmNMo,2470
38
38
  janito/tools/str_replace_editor/handlers/insert.py,sha256=eKHodm2ozKUlRMxWMLAsu9ca6unUo1jfXWwHSld-pSU,4061
39
- janito/tools/str_replace_editor/handlers/str_replace.py,sha256=quHQ1vOqvXCPaC14W5iub01kKB4XNMqwh1odnXnhhxY,4459
39
+ janito/tools/str_replace_editor/handlers/str_replace.py,sha256=RciLTlA7R2PGljeeyluLcBHUAje9c1OCxm-bFE7j6iY,4473
40
40
  janito/tools/str_replace_editor/handlers/undo.py,sha256=3OIdAWkpXC2iDe94_sfx_WxEFh3a1cRzoP0NtPXq1Ks,2491
41
- janito/tools/str_replace_editor/handlers/view.py,sha256=ZWcx7r5VwxWG3ImQxK2lzONL9xlqctM-hy2kMF-huT0,6616
42
- janito/data/instructions_template.txt,sha256=oXmjVEajwLuTjcyXyD123CGSjNG1QLbfzbw9iEq-w5w,1411
43
- janito-0.12.0.dist-info/METADATA,sha256=oR3nvTukvj3uPVcZ2LbAA4lx46W08elLhWphDWXpAAs,6458
44
- janito-0.12.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
45
- janito-0.12.0.dist-info/entry_points.txt,sha256=JMbF_1jg-xQddidpAYkzjOKdw70fy_ymJfcmerY2wIY,47
46
- janito-0.12.0.dist-info/licenses/LICENSE,sha256=6-H8LXExbBIAuT4cyiE-Qy8Bad1K4pagQRVTWr6wkhk,1096
47
- janito-0.12.0.dist-info/RECORD,,
41
+ janito/tools/str_replace_editor/handlers/view.py,sha256=k8F-n64bomHmDjavr5OJKC4cHhfm4_1-aMIFZdMICQo,6809
42
+ janito/data/instructions_template.txt,sha256=FlyGpNjl2LRBjPWLTK40oKOSH6sveHp5ud35nf_lfs8,1716
43
+ janito-0.13.0.dist-info/METADATA,sha256=06Vh36jWdwTcsUOpIyRkHybxssdksSoWNvPVE41y1jM,10584
44
+ janito-0.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
45
+ janito-0.13.0.dist-info/entry_points.txt,sha256=JMbF_1jg-xQddidpAYkzjOKdw70fy_ymJfcmerY2wIY,47
46
+ janito-0.13.0.dist-info/licenses/LICENSE,sha256=6-H8LXExbBIAuT4cyiE-Qy8Bad1K4pagQRVTWr6wkhk,1096
47
+ janito-0.13.0.dist-info/RECORD,,