janito 0.10.0__py3-none-any.whl → 0.11.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.
@@ -5,7 +5,7 @@ import os
5
5
  import pathlib
6
6
  from typing import Dict, Any, Tuple
7
7
  from janito.config import get_config
8
- from .utils import normalize_path, _file_history, backup_file, get_backup_info
8
+ from .utils import normalize_path, _file_history
9
9
 
10
10
  def handle_create(args: Dict[str, Any]) -> Tuple[str, bool]:
11
11
  """
@@ -30,9 +30,9 @@ def handle_create(args: Dict[str, Any]) -> Tuple[str, bool]:
30
30
  # Convert to Path object for better path handling
31
31
  file_path = pathlib.Path(path)
32
32
 
33
- # Check if the file already exists
34
- if file_path.exists():
35
- return (f"File {path} already exists", True)
33
+ # Check if the file already exists - according to spec, create cannot be used if file exists
34
+ if file_path.exists() and file_path.is_file():
35
+ return (f"File {path} already exists. The 'create' command cannot be used if the specified path already exists as a file.", True)
36
36
 
37
37
  # Create parent directories if they don't exist
38
38
  file_path.parent.mkdir(parents=True, exist_ok=True)
@@ -72,35 +72,48 @@ def handle_view(args: Dict[str, Any]) -> Tuple[str, bool]:
72
72
  if not file_path.exists():
73
73
  return (f"File or directory {path} does not exist", True)
74
74
 
75
- # If the path is a directory, list its contents
75
+ # If the path is a directory, list non-hidden files and directories up to 2 levels deep
76
76
  if file_path.is_dir():
77
77
  try:
78
- # Get all files and directories in the directory
79
- items = list(file_path.iterdir())
78
+ result = []
79
+ # Process the first level
80
+ for item in sorted(file_path.iterdir()):
81
+ if item.name.startswith('.'):
82
+ continue # Skip hidden files/directories
83
+
84
+ if item.is_dir():
85
+ result.append(f"{item.name}/")
86
+ # Process the second level
87
+ try:
88
+ for subitem in sorted(item.iterdir()):
89
+ if subitem.name.startswith('.'):
90
+ continue # Skip hidden files/directories
91
+
92
+ if subitem.is_dir():
93
+ result.append(f"{item.name}/{subitem.name}/")
94
+ else:
95
+ result.append(f"{item.name}/{subitem.name}")
96
+ except PermissionError:
97
+ # Skip directories we can't access
98
+ pass
99
+ else:
100
+ result.append(item.name)
80
101
 
81
- # Sort items (directories first, then files)
82
- dirs = [item.name + "/" for item in items if item.is_dir()]
83
- files = [item.name for item in items if item.is_file()]
102
+ if not result:
103
+ return (f"Directory {path} is empty or contains only hidden files", False)
84
104
 
85
- dirs.sort()
86
- files.sort()
105
+ # Determine if we need to truncate the output
106
+ MAX_LINES = 100 # Arbitrary limit for demonstration
107
+ output = "\n".join(result)
108
+ if len(result) > MAX_LINES:
109
+ truncated_output = "\n".join(result[:MAX_LINES])
110
+ return (truncated_output + "\n<response clipped>", False)
87
111
 
88
- # Combine the lists
89
- contents = dirs + files
90
-
91
- if not contents:
92
- return (f"Directory {path} is empty", False)
93
-
94
- # Add count information to the output
95
- dir_count = len(dirs)
96
- file_count = len(files)
97
- count_info = f"Total: {len(contents)} ({dir_count} directories, {file_count} files)"
98
-
99
- return ("\n".join(contents) + f"\n{count_info}", False)
112
+ return (output, False)
100
113
  except Exception as e:
101
114
  return (f"Error listing directory {path}: {str(e)}", True)
102
115
 
103
- # If the path is a file, view its contents
116
+ # If the path is a file, view its contents with cat -n style output
104
117
  try:
105
118
  with open(file_path, 'r', encoding='utf-8') as f:
106
119
  content = f.readlines()
@@ -113,34 +126,27 @@ def handle_view(args: Dict[str, Any]) -> Tuple[str, bool]:
113
126
 
114
127
  # Adjust content to only include the specified lines
115
128
  content = content[start_line:end_line]
116
-
117
- # Add line numbers to each line
118
- numbered_content = []
119
- for i, line in enumerate(content):
120
- line_number = start_line + i + 1 # Convert back to 1-indexed
121
- numbered_content.append(f"{line_number}: {line}")
122
-
123
- # Add line count information
124
- line_count = end_line - start_line
125
-
126
- # Show relative path if it's not an absolute path
127
- display_path = path if os.path.isabs(path) else os.path.relpath(file_path, get_config().workspace_dir)
128
- line_info = f"Viewed {line_count} lines from {display_path}"
129
-
130
- return ("".join(numbered_content) + f"\n{line_info}", False)
131
- else:
132
- # Add line numbers to each line
133
- numbered_content = []
134
- for i, line in enumerate(content):
135
- line_number = i + 1 # 1-indexed line numbers
136
- numbered_content.append(f"{line_number}: {line}")
137
-
138
- # Add line count information
139
- # Show relative path if it's not an absolute path
140
- display_path = path if os.path.isabs(path) else os.path.relpath(file_path, get_config().workspace_dir)
141
- line_info = f"Viewed {len(content)} lines from {display_path}"
142
-
143
- return ("".join(numbered_content) + f"\n{line_info}", False)
129
+
130
+ # Add line numbers to each line (cat -n style)
131
+ numbered_content = []
132
+ start_idx = 1 if view_range is None else view_range[0]
133
+ for i, line in enumerate(content):
134
+ line_number = start_idx + i
135
+ # Ensure line ends with newline
136
+ if not line.endswith('\n'):
137
+ line += '\n'
138
+ numbered_content.append(f"{line_number:6d}\t{line}")
139
+
140
+ # Show relative path if it's not an absolute path
141
+ display_path = path if os.path.isabs(path) else os.path.relpath(file_path, get_config().workspace_dir)
142
+
143
+ # Check if we need to truncate the output
144
+ MAX_LINES = 500 # Arbitrary limit for demonstration
145
+ if len(numbered_content) > MAX_LINES:
146
+ truncated_content = "".join(numbered_content[:MAX_LINES])
147
+ return (truncated_content + "\n<response clipped>", False)
148
+
149
+ return ("".join(numbered_content), False)
144
150
  except Exception as e:
145
151
  return (f"Error viewing file {path}: {str(e)}", True)
146
152
 
@@ -152,7 +158,7 @@ def handle_str_replace(args: Dict[str, Any]) -> Tuple[str, bool]:
152
158
  Args:
153
159
  args: Dictionary containing:
154
160
  - path: Path to the file to modify
155
- - old_str: The text to replace
161
+ - old_str: The text to replace (must match EXACTLY)
156
162
  - new_str: The new text to insert
157
163
 
158
164
  Returns:
@@ -160,14 +166,12 @@ def handle_str_replace(args: Dict[str, Any]) -> Tuple[str, bool]:
160
166
  """
161
167
  path = args.get("path")
162
168
  old_str = args.get("old_str")
163
- new_str = args.get("new_str")
169
+ new_str = args.get("new_str", "") # new_str can be empty to effectively delete text
164
170
 
165
171
  if not path:
166
172
  return ("Missing required parameter: path", True)
167
173
  if old_str is None:
168
174
  return ("Missing required parameter: old_str", True)
169
- if new_str is None:
170
- return ("Missing required parameter: new_str", True)
171
175
 
172
176
  path = normalize_path(path)
173
177
  file_path = pathlib.Path(path)
@@ -180,22 +184,19 @@ def handle_str_replace(args: Dict[str, Any]) -> Tuple[str, bool]:
180
184
  with open(file_path, 'r', encoding='utf-8') as f:
181
185
  content = f.read()
182
186
 
183
- # Backup the file before making changes
184
- backup_file(path, content)
185
-
186
- # Save the current content for undo (legacy approach, will be deprecated)
187
+ # Save the current content for undo
187
188
  if path not in _file_history:
188
189
  _file_history[path] = []
189
190
  _file_history[path].append(content)
190
191
 
191
- # Check if old_str exists in the content
192
+ # Check if old_str exists in the content (must match EXACTLY)
192
193
  if old_str not in content:
193
- return ("Error: No match found for replacement. Please check your text and try again.", True)
194
+ return ("Error: No exact match found for replacement. Please check your text and ensure whitespaces match exactly.", True)
194
195
 
195
196
  # Count occurrences to check for multiple matches
196
197
  match_count = content.count(old_str)
197
198
  if match_count > 1:
198
- return (f"Error: Found {match_count} matches for replacement text. Please provide more context to make a unique match.", True)
199
+ return (f"Error: Found {match_count} matches for replacement text. The old_str parameter is not unique in the file. Please include more context to make it unique.", True)
199
200
 
200
201
  # Replace the string
201
202
  new_content = content.replace(old_str, new_str)
@@ -204,9 +205,13 @@ def handle_str_replace(args: Dict[str, Any]) -> Tuple[str, bool]:
204
205
  with open(file_path, 'w', encoding='utf-8') as f:
205
206
  f.write(new_content)
206
207
 
207
- return (f"Successfully replaced string in file {path}", False)
208
+ # Show relative path if it's not an absolute path in the original input
209
+ display_path = args.get("path") if os.path.isabs(args.get("path")) else os.path.relpath(file_path, get_config().workspace_dir)
210
+ return (f"Successfully replaced string in file {display_path}", False)
208
211
  except Exception as e:
209
- return (f"Error replacing string in file {path}: {str(e)}", True)
212
+ # Show relative path if it's not an absolute path in the original input
213
+ display_path = args.get("path") if os.path.isabs(args.get("path")) else os.path.relpath(file_path, get_config().workspace_dir)
214
+ return (f"Error replacing string in file {display_path}: {str(e)}", True)
210
215
 
211
216
 
212
217
  def handle_insert(args: Dict[str, Any]) -> Tuple[str, bool]:
@@ -233,13 +238,11 @@ def handle_insert(args: Dict[str, Any]) -> Tuple[str, bool]:
233
238
  if new_str is None:
234
239
  return ("Missing required parameter: new_str", True)
235
240
 
236
- # Get the workspace directory from config
237
- workspace_dir = get_config().workspace_dir
238
-
239
- # Make path absolute if it's not already
240
- if not os.path.isabs(path):
241
- path = os.path.join(workspace_dir, path)
241
+ # Store the original path for display purposes
242
+ original_path = path
242
243
 
244
+ # Normalize the path (converts to absolute path)
245
+ path = normalize_path(path)
243
246
  file_path = pathlib.Path(path)
244
247
 
245
248
  if not file_path.exists():
@@ -251,10 +254,7 @@ def handle_insert(args: Dict[str, Any]) -> Tuple[str, bool]:
251
254
  lines = f.readlines()
252
255
  content = "".join(lines)
253
256
 
254
- # Backup the file before making changes
255
- backup_file(path, content)
256
-
257
- # Save the current content for undo (legacy approach, will be deprecated)
257
+ # Save the current content for undo
258
258
  if path not in _file_history:
259
259
  _file_history[path] = []
260
260
  _file_history[path].append(content)
@@ -274,14 +274,23 @@ def handle_insert(args: Dict[str, Any]) -> Tuple[str, bool]:
274
274
  with open(file_path, 'w', encoding='utf-8') as f:
275
275
  f.writelines(lines)
276
276
 
277
- return (f"Successfully inserted text at line {insert_line} in file {path}", False)
277
+ # Show relative path if it's not an absolute path in the original input
278
+ display_path = original_path if os.path.isabs(original_path) else os.path.relpath(file_path, get_config().workspace_dir)
279
+
280
+ # If the response is too long, truncate it
281
+ response = f"Successfully inserted text at line {insert_line} in file {display_path}"
282
+ if len(response) > 1000: # Arbitrary limit for demonstration
283
+ return (response[:1000] + "\n<response clipped>", False)
284
+
285
+ return (response, False)
278
286
  except Exception as e:
279
- return (f"Error inserting text in file {path}: {str(e)}", True)
287
+ display_path = original_path if os.path.isabs(original_path) else os.path.relpath(file_path, get_config().workspace_dir)
288
+ return (f"Error inserting text in file {display_path}: {str(e)}", True)
280
289
 
281
290
 
282
291
  def handle_undo_edit(args: Dict[str, Any]) -> Tuple[str, bool]:
283
292
  """
284
- Undo the last edit made to a file.
293
+ Undo the last edit made to a file using in-memory history.
285
294
 
286
295
  Args:
287
296
  args: Dictionary containing:
@@ -295,33 +304,18 @@ def handle_undo_edit(args: Dict[str, Any]) -> Tuple[str, bool]:
295
304
  if not path:
296
305
  return ("Missing required parameter: path", True)
297
306
 
298
- # Get the workspace directory from config
299
- workspace_dir = get_config().workspace_dir
307
+ # Store the original path for display purposes
308
+ original_path = path
300
309
 
301
- # Make path absolute if it's not already
302
- if not os.path.isabs(path):
303
- path = os.path.join(workspace_dir, path)
310
+ # Normalize the path (converts to absolute path)
311
+ path = normalize_path(path)
312
+ file_path = pathlib.Path(path)
304
313
 
305
- # First try to use the file-based backup system
306
- backup_info = get_backup_info()
307
- if backup_info:
308
- backup_path = backup_info['path']
309
- backup_content = backup_info['content']
310
-
311
- # If a path was provided, check if it matches the backup
312
- if path != backup_path:
313
- return (f"No backup found for file {path}. Last edited file was {backup_path}", True)
314
-
315
- try:
316
- # Write the backup content back to the file
317
- with open(path, 'w', encoding='utf-8') as f:
318
- f.write(backup_content)
319
-
320
- return (f"Successfully undid last edit to file {path}", False)
321
- except Exception as e:
322
- return (f"Error undoing edit to file {path}: {str(e)}", True)
314
+ # Check if file exists
315
+ if not file_path.exists():
316
+ return (f"File {path} does not exist", True)
323
317
 
324
- # Fall back to the in-memory history if no file backup exists
318
+ # Check in-memory history
325
319
  if path not in _file_history or not _file_history[path]:
326
320
  return (f"No edit history for file {path}", True)
327
321
 
@@ -333,6 +327,9 @@ def handle_undo_edit(args: Dict[str, Any]) -> Tuple[str, bool]:
333
327
  with open(path, 'w', encoding='utf-8') as f:
334
328
  f.write(last_content)
335
329
 
336
- return (f"Successfully undid last edit to file {path}", False)
330
+ # Show relative path if it's not an absolute path in the original input
331
+ display_path = original_path if os.path.isabs(original_path) else os.path.relpath(file_path, get_config().workspace_dir)
332
+ return (f"Successfully reverted the last edit made to the file {display_path}", False)
337
333
  except Exception as e:
338
- return (f"Error undoing edit to file {path}: {str(e)}", True)
334
+ display_path = original_path if os.path.isabs(original_path) else os.path.relpath(file_path, get_config().workspace_dir)
335
+ return (f"Error undoing edit to file {display_path}: {str(e)}", True)
@@ -2,19 +2,21 @@
2
2
  Utility functions for the str_replace_editor package.
3
3
  """
4
4
  import os
5
- import pathlib
6
- from typing import Dict, Any, Optional
5
+ from typing import Dict
7
6
  from janito.config import get_config
8
7
 
9
8
  def normalize_path(path: str) -> str:
10
9
  """
11
10
  Normalizes a path relative to the workspace directory.
12
11
 
12
+ For internal operations, converts relative paths to absolute paths
13
+ based on the workspace directory.
14
+
13
15
  Args:
14
16
  path: The original path
15
17
 
16
18
  Returns:
17
- The normalized path relative to the workspace directory
19
+ The normalized absolute path
18
20
  """
19
21
  # If path is absolute, return it as is
20
22
  if os.path.isabs(path):
@@ -24,65 +26,9 @@ def normalize_path(path: str) -> str:
24
26
  if path.startswith('./'):
25
27
  path = path[2:]
26
28
 
27
- # For relative paths, we should keep them relative
28
- # Only prepend workspace_dir if we need to resolve the path
29
- # against the workspace directory
30
- return path
31
-
32
- def backup_file(file_path: str, content: str) -> None:
33
- """
34
- Backup a file before editing it.
35
-
36
- Args:
37
- file_path: Path to the file being edited
38
- content: Current content of the file
39
- """
40
- # Get workspace directory
41
- workspace_dir = get_config().workspace_dir
42
-
43
- # Create .janito/undo directory in the workspace if it doesn't exist
44
- backup_dir = pathlib.Path(workspace_dir) / ".janito" / "undo"
45
- backup_dir.mkdir(parents=True, exist_ok=True)
46
-
47
- # Store the original path
48
- path_file = backup_dir / "path"
49
- with open(path_file, 'w', encoding='utf-8') as f:
50
- f.write(file_path)
51
-
52
- # Store the original content
53
- content_file = backup_dir / "content"
54
- with open(content_file, 'w', encoding='utf-8') as f:
55
- f.write(content)
56
-
57
- def get_backup_info() -> Optional[Dict[str, str]]:
58
- """
59
- Get the backup information for the last edited file.
60
-
61
- Returns:
62
- Dictionary with 'path' and 'content' keys, or None if no backup exists
63
- """
64
- # Get workspace directory
29
+ # Convert relative paths to absolute paths for internal operations
65
30
  workspace_dir = get_config().workspace_dir
66
-
67
- path_file = pathlib.Path(workspace_dir) / ".janito" / "undo" / "path"
68
- content_file = pathlib.Path(workspace_dir) / ".janito" / "undo" / "content"
69
-
70
- if not path_file.exists() or not content_file.exists():
71
- return None
72
-
73
- try:
74
- with open(path_file, 'r', encoding='utf-8') as f:
75
- path = f.read()
76
-
77
- with open(content_file, 'r', encoding='utf-8') as f:
78
- content = f.read()
79
-
80
- return {
81
- 'path': path,
82
- 'content': content
83
- }
84
- except Exception:
85
- return None
31
+ return os.path.normpath(os.path.join(workspace_dir, path))
86
32
 
87
- # Store file history for undo operations (in-memory backup, will be deprecated)
33
+ # Store file history for undo operations (in-memory backup)
88
34
  _file_history = {}
@@ -1,88 +1,86 @@
1
- Metadata-Version: 2.2
2
- Name: janito
3
- Version: 0.10.0
4
- Summary: Janito CLI tool
5
- Author-email: João Pinto <lamego.pinto@gmail.com>
6
- Project-URL: Homepage, https://github.com/joaompinto/janito
7
- Requires-Python: >=3.8
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- Requires-Dist: typer>=0.9.0
11
- Requires-Dist: rich>=13.0.0
12
- Requires-Dist: claudine>=0.1.0
13
-
14
- # 🤖 Janito
15
-
16
- Janito is a powerful AI-assisted command-line interface (CLI) tool built with Python, leveraging Anthropic's Claude for intelligent code and file management.
17
-
18
- ## ✨ Features
19
-
20
- - 🧠 Intelligent AI assistant powered by Claude
21
- - 📁 File management capabilities
22
- - 🔍 Smart code search and editing
23
- - 💻 Interactive terminal interface with rich formatting
24
- - 📊 Token usage tracking and cost reporting
25
-
26
- ## 🛠️ Installation
27
-
28
- ```bash
29
- # Clone the repository
30
- git clone https://github.com/joaompinto/janito.git
31
- cd janito
32
-
33
- # Install the package
34
- pip install -e .
35
- ```
36
-
37
- ## 🚀 Usage
38
-
39
- After installation, you can use the `janito` command in your terminal:
40
-
41
- ```bash
42
- # Get help
43
- janito --help
44
-
45
-
46
- # Ask the AI assistant a question
47
- janito "Suggest improvements to this project"
48
-
49
- janito "Add a --version to the cli to report he version"
50
-
51
- ```
52
-
53
- ## 🔧 Available Tools
54
-
55
- Janito comes with several built-in tools:
56
- - 📄 `str_replace_editor` - View, create, and edit files
57
- - 🔎 `find_files` - Find files matching patterns
58
- - 🗑️ `delete_file` - Delete files
59
- - 🔍 `search_text` - Search for text patterns in files
60
-
61
- ## ⚙️ Requirements
62
-
63
- - Python 3.8 or higher
64
- - Dependencies:
65
- - typer (>=0.9.0)
66
- - rich (>=13.0.0)
67
- - claudine (for Claude AI integration)
68
-
69
- ## 🔑 API Key
70
-
71
- Janito requires an Anthropic API key to function. You can:
72
- 1. Set it as an environment variable: `export ANTHROPIC_API_KEY=your_api_key`
73
- 2. Or enter it when prompted
74
-
75
- ## 💻 Development
76
-
77
- ```bash
78
- # Create a virtual environment
79
- python -m venv .venv
80
- source .venv/bin/activate # On Windows: .venv\Scripts\activate
81
-
82
- # Install development dependencies
83
- pip install -e ".[dev]"
84
- ```
85
-
86
- ## 📜 License
87
-
88
- [Add your license information here]
1
+ Metadata-Version: 2.4
2
+ Name: janito
3
+ Version: 0.11.0
4
+ Summary: Janito CLI tool
5
+ Project-URL: Homepage, https://github.com/joaompinto/janito
6
+ Author-email: João Pinto <lamego.pinto@gmail.com>
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.8
9
+ Requires-Dist: claudine>=0.1.0
10
+ Requires-Dist: rich>=13.0.0
11
+ Requires-Dist: typer>=0.9.0
12
+ Description-Content-Type: text/markdown
13
+
14
+ # 🤖 Janito
15
+
16
+ Janito is a powerful AI-assisted command-line interface (CLI) tool built with Python, leveraging Anthropic's Claude for intelligent code and file management.
17
+
18
+ ## ✨ Features
19
+
20
+ - 🧠 Intelligent AI assistant powered by Claude
21
+ - 📁 File management capabilities
22
+ - 🔍 Smart code search and editing
23
+ - 💻 Interactive terminal interface with rich formatting
24
+ - 📊 Token usage tracking and cost reporting
25
+
26
+ ## 🛠️ Installation
27
+
28
+ ```bash
29
+ # Install directly from PyPI
30
+ pip install janito
31
+ ```
32
+
33
+ For development or installation from source, please see [README_DEV.md](README_DEV.md).
34
+
35
+ ## 🚀 Usage
36
+
37
+ After installation, you can use the `janito` command in your terminal:
38
+
39
+ ```bash
40
+ # Get help
41
+ janito --help
42
+
43
+
44
+ # Ask the AI assistant a question
45
+ janito "Suggest improvements to this project"
46
+
47
+ janito "Add a --version to the cli to report he version"
48
+
49
+ ```
50
+
51
+ ## 🔧 Available Tools
52
+
53
+ Janito comes with several built-in tools:
54
+ - 📄 `str_replace_editor` - View, create, and edit files
55
+ - 🔎 `find_files` - Find files matching patterns
56
+ - 🗑️ `delete_file` - Delete files
57
+ - 🔍 `search_text` - Search for text patterns in files
58
+
59
+ ## ⚙️ Requirements
60
+
61
+ - Python 3.8 or higher
62
+ - Dependencies:
63
+ - typer (>=0.9.0)
64
+ - rich (>=13.0.0)
65
+ - claudine (for Claude AI integration)
66
+
67
+ ## 🔑 API Key
68
+
69
+ Janito requires an Anthropic API key to function. You can:
70
+ 1. Set it as an environment variable: `export ANTHROPIC_API_KEY=your_api_key`
71
+ 2. Or enter it when prompted
72
+
73
+ ## 💻 Development
74
+
75
+ ```bash
76
+ # Create a virtual environment
77
+ python -m venv .venv
78
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
79
+
80
+ # Install development dependencies
81
+ pip install -e ".[dev]"
82
+ ```
83
+
84
+ ## 📜 License
85
+
86
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,26 @@
1
+ janito/__init__.py,sha256=Ral9Ev43UBuUjoDGLTUMJ62fAiWUC6zMMkOKTkzAQmo,53
2
+ janito/__main__.py,sha256=IvUnkU8o5JEvwDkv-gXgPXdQWVubY7-0tiVOZrieu-s,8779
3
+ janito/callbacks.py,sha256=5SoXyyvksSK_xusch3Wb5dIBohChAD2g3B51CehUxYY,4916
4
+ janito/chat_history.py,sha256=LH1vBq472Duw3kis8FushjwZtI052IPsFZcKwaVjytM,3622
5
+ janito/config.py,sha256=ZKW4PtiSkayaXaT0jtLFUALl3CnXfgqFwxRwuZfb0es,4432
6
+ janito/test_file.py,sha256=c6GWGdTYG3z-Y5XBao9Tmhmq3G-v0L37OfwLgBo8zIU,126
7
+ janito/token_report.py,sha256=1ja93AGTMCXNV0WMUq2nmstKrrt9UEukjheOUVKEmzY,8842
8
+ janito/data/instructions.txt,sha256=ivNjw7s5qXYqV7K8ZLlPII_XSQSvtWq7WCHx3sr0b-I,232
9
+ janito/tools/__init__.py,sha256=XW9EaEishvAf4qtylJquti77Q8OL3dVyAIPO_LUUFFg,646
10
+ janito/tools/bash.py,sha256=FkVUYaPfo_yZ2zUiGKtE_7sPKJlWVBdtIdCB7m7-g4s,737
11
+ janito/tools/decorators.py,sha256=SMirsokAwesiS4ZoZhxXSenMR78Vh-C9PGLpyvW0Xwo,3095
12
+ janito/tools/delete_file.py,sha256=L5oilAuO3OtJ1qhmtRO-lVQMwDKteP-P3S55PI9g-AE,1442
13
+ janito/tools/find_files.py,sha256=VDoait4sMaJtQdECltTQzOAN_5FRpp8j5bFTznOnbcw,6280
14
+ janito/tools/prompt_user.py,sha256=TMa9BZk5t8AGwj_Zzxu9ciIXm1J0PhRMolGv6V5MPUA,738
15
+ janito/tools/replace_file.py,sha256=NFlmrvXagFwxfDk1Py-qZYLMXDkc_MiPAzaeQqvDBtk,1115
16
+ janito/tools/search_text.py,sha256=d3iYMGHS7ZeMJxek4A_7DwMrnsWkp_XOtp3bkyNor0M,9292
17
+ janito/tools/str_replace_editor/__init__.py,sha256=kYmscmQgft3Jzt3oCNz7k2FiRbJvku6OFDDC3Q_zoAA,144
18
+ janito/tools/str_replace_editor/editor.py,sha256=DqoK5Yn_3hClisUOkRfSIMvkPVofBuGIe8UpD93K9W0,2306
19
+ janito/tools/str_replace_editor/handlers.py,sha256=2HlBAnzg6S8YSfGtGZuQp857Rfn6nybew3TWf5TPnuI,13395
20
+ janito/tools/str_replace_editor/utils.py,sha256=h-unWcrVEcH0xgsW-iJuApUa0Lc4gsPyBlmtn2mw64I,979
21
+ janito/data/instructions.txt,sha256=ivNjw7s5qXYqV7K8ZLlPII_XSQSvtWq7WCHx3sr0b-I,232
22
+ janito-0.11.0.dist-info/METADATA,sha256=pTblnOR4OX2AqiuV6wyDPj7gDD2vRxDx4CvLLa281Nc,2146
23
+ janito-0.11.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
24
+ janito-0.11.0.dist-info/entry_points.txt,sha256=JMbF_1jg-xQddidpAYkzjOKdw70fy_ymJfcmerY2wIY,47
25
+ janito-0.11.0.dist-info/licenses/LICENSE,sha256=6-H8LXExbBIAuT4cyiE-Qy8Bad1K4pagQRVTWr6wkhk,1096
26
+ janito-0.11.0.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.0.0)
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-