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.
@@ -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
@@ -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="Editing file: {file_path} ({command})")
16
- def str_replace_editor(**kwargs) -> Tuple[str, bool]:
17
- """
18
- Handle text editor tool requests from Claude.
19
- Implements the Claude text editor tool specification.
20
-
21
- Args:
22
- **kwargs: All arguments passed to the tool, including:
23
- - command: The command to execute (view, create, str_replace, insert, undo_edit)
24
- - path: Path to the file
25
- - Additional command-specific arguments
26
-
27
- Returns:
28
- A tuple containing (message, is_error)
29
- """
30
- command = kwargs.get("command")
31
-
32
- if command == "create":
33
- return handle_create(kwargs)
34
- elif command == "view":
35
- return handle_view(kwargs)
36
- elif command == "str_replace":
37
- return handle_str_replace(kwargs)
38
- elif command == "insert":
39
- return handle_insert(kwargs)
40
- elif command == "undo_edit":
41
- return handle_undo_edit(kwargs)
42
- else:
43
- return (f"Command '{command}' not implemented yet", True)
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)