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.
@@ -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 format_tool_label(func: Callable, tool_input: Dict[str, Any]) -> Optional[str]:
53
- """
54
- Format the tool label using the function's parameters.
55
-
56
- Args:
57
- func: The tool function
58
- tool_input: Input parameters for the tool
59
-
60
- Returns:
61
- Formatted label string or None if no label is defined
62
- """
63
- if not hasattr(func, '_tool_meta') or 'label' not in func._tool_meta:
64
- return None
65
-
66
- # Get the label template
67
- label_template = func._tool_meta['label']
68
-
69
- # Special handling for str_replace_editor which uses **kwargs
70
- if func.__name__ == 'str_replace_editor':
71
- # Extract command and file_path from tool_input if they exist
72
- command = tool_input.get('command', 'unknown')
73
- file_path = tool_input.get('file_path', '')
74
-
75
- # Simple string replacement for the common case
76
- if '{command}' in label_template and '{file_path}' in label_template:
77
- return label_template.replace('{command}', command).replace('{file_path}', file_path)
78
-
79
- # Format the label with the parameters
80
- try:
81
- formatter = ToolMetaFormatter()
82
- return formatter.format(label_template, **tool_input)
83
- except Exception as e:
84
- return f"{func.__name__}"
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__}"
@@ -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
- # Normalize the file path
26
- path = normalize_path(file_path)
27
-
28
- # Convert to Path object for better path handling
29
- path_obj = Path(path)
30
-
31
- # Check if the file exists
32
- if not path_obj.exists():
33
- return (f"File {path} does not exist.", True)
34
-
35
- # Check if it's a directory
36
- if path_obj.is_dir():
37
- return (f"{path} is a directory, not a file. Use delete_directory for directories.", True)
38
-
39
- # Delete the file
40
- try:
41
- path_obj.unlink()
42
- return (f"Successfully deleted file {path}", False)
43
- except Exception as e:
44
- return (f"Error deleting file {path}: {str(e)}", True)
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)
@@ -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 name matches a glob pattern.
10
+ Find files whose path matches a glob pattern.
11
11
 
12
12
  Args:
13
- pattern: pattern to match file names against
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 fnmatch.filter(filenames, pattern):
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
- matching_files.append(rel_path)
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 fnmatch.filter(os.listdir(abs_root), pattern):
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
- matching_files.append(rel_path)
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