janito 0.10.1__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.
- janito/__main__.py +205 -151
- janito/callbacks.py +132 -130
- janito/chat_history.py +117 -0
- janito/config.py +64 -6
- janito/data/instructions.txt +4 -4
- janito/token_report.py +145 -73
- janito/tools/__init__.py +21 -10
- janito/tools/bash.py +22 -0
- janito/tools/decorators.py +101 -84
- janito/tools/delete_file.py +47 -44
- janito/tools/find_files.py +11 -7
- janito/tools/prompt_user.py +26 -0
- janito/tools/replace_file.py +36 -0
- janito/tools/str_replace_editor/editor.py +52 -43
- janito/tools/str_replace_editor/handlers.py +102 -105
- janito/tools/str_replace_editor/utils.py +8 -62
- {janito-0.10.1.dist-info → janito-0.11.0.dist-info}/METADATA +1 -1
- janito-0.11.0.dist-info/RECORD +26 -0
- janito/cli.py +0 -202
- janito-0.10.1.dist-info/RECORD +0 -23
- {janito-0.10.1.dist-info → janito-0.11.0.dist-info}/WHEEL +0 -0
- {janito-0.10.1.dist-info → janito-0.11.0.dist-info}/entry_points.txt +0 -0
- {janito-0.10.1.dist-info → janito-0.11.0.dist-info}/licenses/LICENSE +0 -0
janito/tools/decorators.py
CHANGED
@@ -1,84 +1,101 @@
|
|
1
|
-
"""
|
2
|
-
Decorators for janito tools.
|
3
|
-
"""
|
4
|
-
import functools
|
5
|
-
import inspect
|
6
|
-
import string
|
7
|
-
from typing import Any, Callable, Dict, Optional, Tuple
|
8
|
-
|
9
|
-
|
10
|
-
class ToolMetaFormatter(string.Formatter):
|
11
|
-
"""Custom string formatter that handles conditional expressions in format strings."""
|
12
|
-
|
13
|
-
def get_value(self, key, args, kwargs):
|
14
|
-
"""Override to handle conditional expressions."""
|
15
|
-
if key in kwargs:
|
16
|
-
return kwargs[key]
|
17
|
-
|
18
|
-
# Try to evaluate the key as a Python expression
|
19
|
-
try:
|
20
|
-
# Create a safe local namespace with only the parameters
|
21
|
-
return eval(key, {"__builtins__": {}}, kwargs)
|
22
|
-
except Exception:
|
23
|
-
return f"[{key}]"
|
24
|
-
|
25
|
-
|
26
|
-
def tool_meta(label: str):
|
27
|
-
"""
|
28
|
-
Decorator to add metadata to a tool function.
|
29
|
-
|
30
|
-
Args:
|
31
|
-
label: A format string that can reference function parameters.
|
32
|
-
Example: "Finding files {pattern}, on {root_dir}"
|
33
|
-
|
34
|
-
Returns:
|
35
|
-
Decorated function with metadata attached
|
36
|
-
"""
|
37
|
-
def decorator(func: Callable):
|
38
|
-
@functools.wraps(func)
|
39
|
-
def wrapper(*args, **kwargs):
|
40
|
-
return func(*args, **kwargs)
|
41
|
-
|
42
|
-
# Attach metadata to the function
|
43
|
-
wrapper._tool_meta = {
|
44
|
-
'label': label
|
45
|
-
}
|
46
|
-
|
47
|
-
return wrapper
|
48
|
-
|
49
|
-
return decorator
|
50
|
-
|
51
|
-
|
52
|
-
def
|
53
|
-
"""
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
return
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
1
|
+
"""
|
2
|
+
Decorators for janito tools.
|
3
|
+
"""
|
4
|
+
import functools
|
5
|
+
import inspect
|
6
|
+
import string
|
7
|
+
from typing import Any, Callable, Dict, Optional, Tuple
|
8
|
+
|
9
|
+
|
10
|
+
class ToolMetaFormatter(string.Formatter):
|
11
|
+
"""Custom string formatter that handles conditional expressions in format strings."""
|
12
|
+
|
13
|
+
def get_value(self, key, args, kwargs):
|
14
|
+
"""Override to handle conditional expressions."""
|
15
|
+
if key in kwargs:
|
16
|
+
return kwargs[key]
|
17
|
+
|
18
|
+
# Try to evaluate the key as a Python expression
|
19
|
+
try:
|
20
|
+
# Create a safe local namespace with only the parameters
|
21
|
+
return eval(key, {"__builtins__": {}}, kwargs)
|
22
|
+
except Exception:
|
23
|
+
return f"[{key}]"
|
24
|
+
|
25
|
+
|
26
|
+
def tool_meta(label: str):
|
27
|
+
"""
|
28
|
+
Decorator to add metadata to a tool function.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
label: A format string that can reference function parameters.
|
32
|
+
Example: "Finding files {pattern}, on {root_dir}"
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Decorated function with metadata attached
|
36
|
+
"""
|
37
|
+
def decorator(func: Callable):
|
38
|
+
@functools.wraps(func)
|
39
|
+
def wrapper(*args, **kwargs):
|
40
|
+
return func(*args, **kwargs)
|
41
|
+
|
42
|
+
# Attach metadata to the function
|
43
|
+
wrapper._tool_meta = {
|
44
|
+
'label': label
|
45
|
+
}
|
46
|
+
|
47
|
+
return wrapper
|
48
|
+
|
49
|
+
return decorator
|
50
|
+
|
51
|
+
|
52
|
+
def tool(func: Callable):
|
53
|
+
"""
|
54
|
+
Basic decorator for tool functions.
|
55
|
+
|
56
|
+
This decorator marks a function as a tool and can be used for
|
57
|
+
simpler tools that don't need additional metadata.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
Decorated function
|
61
|
+
"""
|
62
|
+
@functools.wraps(func)
|
63
|
+
def wrapper(*args, **kwargs):
|
64
|
+
return func(*args, **kwargs)
|
65
|
+
|
66
|
+
return wrapper
|
67
|
+
|
68
|
+
|
69
|
+
def format_tool_label(func: Callable, tool_input: Dict[str, Any]) -> Optional[str]:
|
70
|
+
"""
|
71
|
+
Format the tool label using the function's parameters.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
func: The tool function
|
75
|
+
tool_input: Input parameters for the tool
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
Formatted label string or None if no label is defined
|
79
|
+
"""
|
80
|
+
if not hasattr(func, '_tool_meta') or 'label' not in func._tool_meta:
|
81
|
+
return None
|
82
|
+
|
83
|
+
# Get the label template
|
84
|
+
label_template = func._tool_meta['label']
|
85
|
+
|
86
|
+
# Special handling for str_replace_editor which uses **kwargs
|
87
|
+
if func.__name__ == 'str_replace_editor':
|
88
|
+
# Extract command and file_path from tool_input if they exist
|
89
|
+
command = tool_input.get('command', 'unknown')
|
90
|
+
file_path = tool_input.get('file_path', '')
|
91
|
+
|
92
|
+
# Simple string replacement for the common case
|
93
|
+
if '{command}' in label_template and '{file_path}' in label_template:
|
94
|
+
return label_template.replace('{command}', command).replace('{file_path}', file_path)
|
95
|
+
|
96
|
+
# Format the label with the parameters
|
97
|
+
try:
|
98
|
+
formatter = ToolMetaFormatter()
|
99
|
+
return formatter.format(label_template, **tool_input)
|
100
|
+
except Exception as e:
|
101
|
+
return f"{func.__name__}"
|
janito/tools/delete_file.py
CHANGED
@@ -1,44 +1,47 @@
|
|
1
|
-
"""
|
2
|
-
Tool for deleting files through the claudine agent.
|
3
|
-
"""
|
4
|
-
import os
|
5
|
-
from pathlib import Path
|
6
|
-
from typing import Dict, Any, Tuple
|
7
|
-
from janito.config import get_config
|
8
|
-
from janito.tools.str_replace_editor.utils import normalize_path
|
9
|
-
from janito.tools.decorators import tool_meta
|
10
|
-
|
11
|
-
|
12
|
-
@tool_meta(label="Deleting file {file_path}")
|
13
|
-
def delete_file(
|
14
|
-
file_path: str,
|
15
|
-
) -> Tuple[str, bool]:
|
16
|
-
"""
|
17
|
-
Delete an existing file.
|
18
|
-
|
19
|
-
Args:
|
20
|
-
file_path: Path to the file to delete, relative to the workspace directory
|
21
|
-
|
22
|
-
Returns:
|
23
|
-
A tuple containing (message, is_error)
|
24
|
-
"""
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
1
|
+
"""
|
2
|
+
Tool for deleting files through the claudine agent.
|
3
|
+
"""
|
4
|
+
import os
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Dict, Any, Tuple
|
7
|
+
from janito.config import get_config
|
8
|
+
from janito.tools.str_replace_editor.utils import normalize_path
|
9
|
+
from janito.tools.decorators import tool_meta
|
10
|
+
|
11
|
+
|
12
|
+
@tool_meta(label="Deleting file {file_path}")
|
13
|
+
def delete_file(
|
14
|
+
file_path: str,
|
15
|
+
) -> Tuple[str, bool]:
|
16
|
+
"""
|
17
|
+
Delete an existing file.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
file_path: Path to the file to delete, relative to the workspace directory
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
A tuple containing (message, is_error)
|
24
|
+
"""
|
25
|
+
# Store the original path for display purposes
|
26
|
+
original_path = file_path
|
27
|
+
|
28
|
+
# Normalize the file path (converts to absolute path)
|
29
|
+
path = normalize_path(file_path)
|
30
|
+
|
31
|
+
# Convert to Path object for better path handling
|
32
|
+
path_obj = Path(path)
|
33
|
+
|
34
|
+
# Check if the file exists
|
35
|
+
if not path_obj.exists():
|
36
|
+
return (f"File {original_path} does not exist.", True)
|
37
|
+
|
38
|
+
# Check if it's a directory
|
39
|
+
if path_obj.is_dir():
|
40
|
+
return (f"{original_path} is a directory, not a file. Use delete_directory for directories.", True)
|
41
|
+
|
42
|
+
# Delete the file
|
43
|
+
try:
|
44
|
+
path_obj.unlink()
|
45
|
+
return (f"Successfully deleted file {original_path}", False)
|
46
|
+
except Exception as e:
|
47
|
+
return (f"Error deleting file {original_path}: {str(e)}", True)
|
janito/tools/find_files.py
CHANGED
@@ -4,13 +4,13 @@ from typing import List, Dict, Any, Tuple
|
|
4
4
|
from janito.tools.decorators import tool_meta
|
5
5
|
|
6
6
|
|
7
|
-
@tool_meta(label="Finding files {pattern}, on {root_dir} ({recursive and 'recursive' or 'non-recursive'}, {respect_gitignore and 'respecting gitignore' or 'ignoring gitignore'})")
|
7
|
+
@tool_meta(label="Finding files matching path pattern {pattern}, on {root_dir} ({recursive and 'recursive' or 'non-recursive'}, {respect_gitignore and 'respecting gitignore' or 'ignoring gitignore'})")
|
8
8
|
def find_files(pattern: str, root_dir: str = ".", recursive: bool = True, respect_gitignore: bool = True) -> Tuple[str, bool]:
|
9
9
|
"""
|
10
|
-
Find files whose
|
10
|
+
Find files whose path matches a glob pattern.
|
11
11
|
|
12
12
|
Args:
|
13
|
-
pattern: pattern to match file
|
13
|
+
pattern: pattern to match file paths against (e.g., "*.py", "*/tools/*.py")
|
14
14
|
root_dir: root directory to start search from (default: current directory)
|
15
15
|
recursive: Whether to search recursively in subdirectories (default: True)
|
16
16
|
respect_gitignore: Whether to respect .gitignore files (default: True)
|
@@ -39,7 +39,7 @@ def find_files(pattern: str, root_dir: str = ".", recursive: bool = True, respec
|
|
39
39
|
if respect_gitignore:
|
40
40
|
dirnames[:] = [d for d in dirnames if not _is_ignored(os.path.join(dirpath, d), ignored_patterns, abs_root)]
|
41
41
|
|
42
|
-
for filename in
|
42
|
+
for filename in filenames:
|
43
43
|
file_path = os.path.join(dirpath, filename)
|
44
44
|
|
45
45
|
# Skip ignored files
|
@@ -48,10 +48,12 @@ def find_files(pattern: str, root_dir: str = ".", recursive: bool = True, respec
|
|
48
48
|
|
49
49
|
# Convert to relative path from root_dir
|
50
50
|
rel_path = os.path.relpath(file_path, abs_root)
|
51
|
-
|
51
|
+
# Match against the relative path, not just the filename
|
52
|
+
if fnmatch.fnmatch(rel_path, pattern):
|
53
|
+
matching_files.append(rel_path)
|
52
54
|
else:
|
53
55
|
# Non-recursive mode - only search in the specified directory
|
54
|
-
for filename in
|
56
|
+
for filename in os.listdir(abs_root):
|
55
57
|
file_path = os.path.join(abs_root, filename)
|
56
58
|
|
57
59
|
# Skip ignored files
|
@@ -61,7 +63,9 @@ def find_files(pattern: str, root_dir: str = ".", recursive: bool = True, respec
|
|
61
63
|
if os.path.isfile(file_path):
|
62
64
|
# Convert to relative path from root_dir
|
63
65
|
rel_path = os.path.relpath(file_path, abs_root)
|
64
|
-
|
66
|
+
# Match against the relative path, not just the filename
|
67
|
+
if fnmatch.fnmatch(rel_path, pattern):
|
68
|
+
matching_files.append(rel_path)
|
65
69
|
|
66
70
|
# Sort the files for consistent output
|
67
71
|
matching_files.sort()
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
Tool for prompting the user for input through the claudine agent.
|
3
|
+
"""
|
4
|
+
from typing import Tuple
|
5
|
+
from janito.tools.decorators import tool_meta
|
6
|
+
|
7
|
+
|
8
|
+
@tool_meta(label="Prompting user with '{prompt_text}'")
|
9
|
+
def prompt_user(
|
10
|
+
prompt_text: str,
|
11
|
+
) -> Tuple[str, bool]:
|
12
|
+
"""
|
13
|
+
Prompt the user for input and return their response.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
prompt_text: Text to display to the user as a prompt
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
A tuple containing (user_response, is_error)
|
20
|
+
"""
|
21
|
+
try:
|
22
|
+
# Print the prompt and get user input
|
23
|
+
user_response = input(f"{prompt_text}")
|
24
|
+
return (user_response, False)
|
25
|
+
except Exception as e:
|
26
|
+
return (f"Error prompting user: {str(e)}", True)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
Replace file tool that overwrites a file with new content.
|
3
|
+
"""
|
4
|
+
import os
|
5
|
+
from typing import Tuple
|
6
|
+
|
7
|
+
from janito.tools.decorators import tool
|
8
|
+
|
9
|
+
|
10
|
+
@tool
|
11
|
+
def replace_file(file_path: str, new_content: str) -> Tuple[str, bool]:
|
12
|
+
"""
|
13
|
+
Replace an existing file with new content.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
file_path: Path to the file to replace, relative to the workspace directory
|
17
|
+
new_content: New content to write to the file
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
A tuple containing (message, is_error)
|
21
|
+
"""
|
22
|
+
try:
|
23
|
+
# Convert relative path to absolute path
|
24
|
+
abs_path = os.path.abspath(file_path)
|
25
|
+
|
26
|
+
# Check if file exists
|
27
|
+
if not os.path.isfile(abs_path):
|
28
|
+
return f"Error: File '{file_path}' does not exist", True
|
29
|
+
|
30
|
+
# Write new content to the file
|
31
|
+
with open(abs_path, 'w', encoding='utf-8') as f:
|
32
|
+
f.write(new_content)
|
33
|
+
|
34
|
+
return f"Successfully replaced file '{file_path}'", False
|
35
|
+
except Exception as e:
|
36
|
+
return f"Error replacing file '{file_path}': {str(e)}", True
|
@@ -1,43 +1,52 @@
|
|
1
|
-
"""
|
2
|
-
Main module for implementing the Claude text editor functionality.
|
3
|
-
"""
|
4
|
-
from typing import Dict, Any, Tuple
|
5
|
-
from .handlers import (
|
6
|
-
handle_create,
|
7
|
-
handle_view,
|
8
|
-
handle_str_replace,
|
9
|
-
handle_insert,
|
10
|
-
handle_undo_edit
|
11
|
-
)
|
12
|
-
from .utils import normalize_path
|
13
|
-
from janito.tools.decorators import tool_meta
|
14
|
-
|
15
|
-
@tool_meta(label="
|
16
|
-
def str_replace_editor(**kwargs) -> Tuple[str, bool]:
|
17
|
-
"""
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
1
|
+
"""
|
2
|
+
Main module for implementing the Claude text editor functionality.
|
3
|
+
"""
|
4
|
+
from typing import Dict, Any, Tuple
|
5
|
+
from .handlers import (
|
6
|
+
handle_create,
|
7
|
+
handle_view,
|
8
|
+
handle_str_replace,
|
9
|
+
handle_insert,
|
10
|
+
handle_undo_edit
|
11
|
+
)
|
12
|
+
from .utils import normalize_path
|
13
|
+
from janito.tools.decorators import tool_meta
|
14
|
+
|
15
|
+
@tool_meta(label="File Command: ({command})")
|
16
|
+
def str_replace_editor(**kwargs) -> Tuple[str, bool]:
|
17
|
+
"""
|
18
|
+
Custom editing tool for viewing, creating and editing files
|
19
|
+
* State is persistent across command calls and discussions with the user
|
20
|
+
* If `path` is a file, `view` displays the result of applying `cat -n`. If `path` is a directory, `view` lists non-hidden files and directories up to 2 levels deep
|
21
|
+
* The `create` command cannot be used if the specified `path` already exists as a file
|
22
|
+
* If a `command` generates a long output, it will be truncated and marked with `<response clipped>`
|
23
|
+
* The `undo_edit` command will revert the last edit made to the file at `path`
|
24
|
+
|
25
|
+
Notes for using the `str_replace` command:
|
26
|
+
* The `old_str` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!
|
27
|
+
* If the `old_str` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in `old_str` to make it unique
|
28
|
+
* The `new_str` parameter should contain the edited lines that should replace the `old_str`
|
29
|
+
|
30
|
+
Args:
|
31
|
+
**kwargs: All arguments passed to the tool, including:
|
32
|
+
- command: The command to execute (view, create, str_replace, insert, undo_edit)
|
33
|
+
- path: Path to the file
|
34
|
+
- Additional command-specific arguments
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
A tuple containing (message, is_error)
|
38
|
+
"""
|
39
|
+
command = kwargs.get("command")
|
40
|
+
|
41
|
+
if command == "create":
|
42
|
+
return handle_create(kwargs)
|
43
|
+
elif command == "view":
|
44
|
+
return handle_view(kwargs)
|
45
|
+
elif command == "str_replace":
|
46
|
+
return handle_str_replace(kwargs)
|
47
|
+
elif command == "insert":
|
48
|
+
return handle_insert(kwargs)
|
49
|
+
elif command == "undo_edit":
|
50
|
+
return handle_undo_edit(kwargs)
|
51
|
+
else:
|
52
|
+
return (f"Command '{command}' not implemented yet", True)
|