ciayn fixes
This commit is contained in:
parent
e45cd78c7f
commit
bd02bffc55
|
|
@ -74,14 +74,15 @@ def get_file_listing(
|
|||
directory: str, limit: Optional[int] = None, include_hidden: bool = False
|
||||
) -> Tuple[List[str], int]:
|
||||
"""
|
||||
Get a list of tracked files in a git repository.
|
||||
Get a list of files in a directory.
|
||||
|
||||
Uses `git ls-files` for efficient file listing that respects .gitignore rules.
|
||||
For git repositories, uses `git ls-files` for efficient file listing that respects .gitignore rules.
|
||||
For non-git directories, falls back to manual file listing using Python's standard library.
|
||||
Returns a tuple containing the list of files (truncated if limit is specified)
|
||||
and the total count of files.
|
||||
|
||||
Args:
|
||||
directory: Path to the git repository
|
||||
directory: Path to the directory
|
||||
limit: Optional maximum number of files to return
|
||||
include_hidden: Whether to include hidden files (starting with .) in the results
|
||||
|
||||
|
|
@ -104,50 +105,74 @@ def get_file_listing(
|
|||
raise DirectoryNotFoundError(f"Not a directory: {directory}")
|
||||
|
||||
# Check if it's a git repository
|
||||
if not is_git_repo(directory):
|
||||
return [], 0
|
||||
is_git = is_git_repo(directory)
|
||||
|
||||
# Get list of files from git ls-files
|
||||
try:
|
||||
# Get both tracked and untracked files
|
||||
tracked_files_process = subprocess.run(
|
||||
["git", "ls-files"],
|
||||
cwd=directory,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
untracked_files_process = subprocess.run(
|
||||
["git", "ls-files", "--others", "--exclude-standard"],
|
||||
cwd=directory,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise GitCommandError(f"Git command failed: {e}")
|
||||
except PermissionError as e:
|
||||
raise DirectoryAccessError(f"Permission denied: {e}")
|
||||
|
||||
# Combine and process the files
|
||||
all_files = []
|
||||
for file in (
|
||||
tracked_files_process.stdout.splitlines()
|
||||
+ untracked_files_process.stdout.splitlines()
|
||||
):
|
||||
file = file.strip()
|
||||
if not file:
|
||||
continue
|
||||
# Skip hidden files unless explicitly included
|
||||
if not include_hidden and (
|
||||
file.startswith(".")
|
||||
or any(part.startswith(".") for part in file.split("/"))
|
||||
|
||||
if is_git:
|
||||
# Get list of files from git ls-files
|
||||
try:
|
||||
# Get both tracked and untracked files
|
||||
tracked_files_process = subprocess.run(
|
||||
["git", "ls-files"],
|
||||
cwd=directory,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
untracked_files_process = subprocess.run(
|
||||
["git", "ls-files", "--others", "--exclude-standard"],
|
||||
cwd=directory,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise GitCommandError(f"Git command failed: {e}")
|
||||
except PermissionError as e:
|
||||
raise DirectoryAccessError(f"Permission denied: {e}")
|
||||
|
||||
# Combine and process the files
|
||||
for file in (
|
||||
tracked_files_process.stdout.splitlines()
|
||||
+ untracked_files_process.stdout.splitlines()
|
||||
):
|
||||
continue
|
||||
# Skip .aider files
|
||||
if ".aider" in file:
|
||||
continue
|
||||
all_files.append(file)
|
||||
file = file.strip()
|
||||
if not file:
|
||||
continue
|
||||
# Skip hidden files unless explicitly included
|
||||
if not include_hidden and (
|
||||
file.startswith(".")
|
||||
or any(part.startswith(".") for part in file.split("/"))
|
||||
):
|
||||
continue
|
||||
# Skip .aider files
|
||||
if ".aider" in file:
|
||||
continue
|
||||
all_files.append(file)
|
||||
else:
|
||||
# Not a git repository, use manual file listing
|
||||
base_path = Path(directory)
|
||||
excluded_dirs = {'.ra-aid', '.venv', '.git', '.aider', '__pycache__'}
|
||||
|
||||
for root, dirs, files in os.walk(directory):
|
||||
# Filter out excluded directories
|
||||
dirs[:] = [d for d in dirs if d not in excluded_dirs and (include_hidden or not d.startswith('.'))]
|
||||
|
||||
# Calculate relative path
|
||||
rel_root = os.path.relpath(root, directory)
|
||||
if rel_root == '.':
|
||||
rel_root = ''
|
||||
|
||||
# Process files
|
||||
for file in files:
|
||||
# Skip hidden files unless explicitly included
|
||||
if not include_hidden and file.startswith('.'):
|
||||
continue
|
||||
|
||||
# Create relative path
|
||||
rel_path = os.path.join(rel_root, file) if rel_root else file
|
||||
all_files.append(rel_path)
|
||||
|
||||
# Remove duplicates and sort
|
||||
all_files = sorted(set(all_files))
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ def is_new_project(directory: str) -> bool:
|
|||
|
||||
A project is considered new if it either:
|
||||
- Is an empty directory
|
||||
- Contains only .git directory, .gitignore file, and/or .ra-aid directory
|
||||
- Contains only .git directory, .gitignore file, .venv directory, and/or .ra-aid directory
|
||||
|
||||
Args:
|
||||
directory: String path to the directory to check
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ You are a ReAct agent. You run in a loop and use ONE of the available functions
|
|||
The result of that function call will be given to you in the next message.
|
||||
Call one function at a time. Function arguments can be complex objects, long strings, etc. if needed.
|
||||
Each tool call you make shall be different from the previous.
|
||||
The user cannot see the results of function calls, so you have to explicitly use a tool like ask_human if you want them to see something.
|
||||
The user cannot see the results of function calls, so you have to explicitly use a tool (function call) if you want them to see something. If you don't know what to do, just make a best guess on what function to call.
|
||||
|
||||
YOU MUST ALWAYS RESPOND WITH A SINGLE LINE OF PYTHON THAT CALLS ONE OF THE AVAILABLE TOOLS.
|
||||
NEVER RETURN AN EMPTY MESSAGE.
|
||||
|
|
|
|||
|
|
@ -185,9 +185,10 @@ def list_directory_tree(
|
|||
exclude_patterns: List[str] = None,
|
||||
) -> str:
|
||||
"""List directory contents in a tree format with optional metadata.
|
||||
If a file path is provided, returns information about just that file.
|
||||
|
||||
Args:
|
||||
path: Directory path to list
|
||||
path: Directory or file path to list
|
||||
max_depth: Maximum depth to traverse (default: 1 for no recursion)
|
||||
follow_links: Whether to follow symbolic links
|
||||
show_size: Show file sizes (default: False)
|
||||
|
|
@ -200,24 +201,38 @@ def list_directory_tree(
|
|||
root_path = Path(path).resolve()
|
||||
if not root_path.exists():
|
||||
raise ValueError(f"Path does not exist: {path}")
|
||||
if not root_path.is_dir():
|
||||
raise ValueError(f"Path is not a directory: {path}")
|
||||
|
||||
# Load .gitignore patterns if present
|
||||
spec = load_gitignore_patterns(root_path)
|
||||
# Load .gitignore patterns if present (only needed for directories)
|
||||
spec = None
|
||||
if root_path.is_dir():
|
||||
spec = load_gitignore_patterns(root_path)
|
||||
# Create tree for directory
|
||||
tree = Tree(f"📁 {root_path}/")
|
||||
config = DirScanConfig(
|
||||
max_depth=max_depth,
|
||||
follow_links=follow_links,
|
||||
show_size=show_size,
|
||||
show_modified=show_modified,
|
||||
exclude_patterns=DEFAULT_EXCLUDE_PATTERNS + (exclude_patterns or []),
|
||||
)
|
||||
# Build tree
|
||||
build_tree(root_path, tree, config, 0, spec)
|
||||
else:
|
||||
# Create a simple tree for a single file
|
||||
tree = Tree(f"🗋 {root_path.parent}/")
|
||||
file_text = root_path.name
|
||||
|
||||
# Create tree
|
||||
tree = Tree(f"📁 {root_path}/")
|
||||
config = DirScanConfig(
|
||||
max_depth=max_depth,
|
||||
follow_links=follow_links,
|
||||
show_size=show_size,
|
||||
show_modified=show_modified,
|
||||
exclude_patterns=DEFAULT_EXCLUDE_PATTERNS + (exclude_patterns or []),
|
||||
)
|
||||
# Add size information if requested
|
||||
if show_size:
|
||||
size_str = format_size(root_path.stat().st_size)
|
||||
file_text = f"{file_text} ({size_str})"
|
||||
|
||||
# Build tree
|
||||
build_tree(root_path, tree, config, 0, spec)
|
||||
# Add modified time if requested
|
||||
if show_modified:
|
||||
mod_time = format_time(root_path.stat().st_mtime)
|
||||
file_text = f"{file_text} [Modified: {mod_time}]"
|
||||
|
||||
tree.add(file_text)
|
||||
|
||||
# Capture tree output
|
||||
with console.capture() as capture:
|
||||
|
|
|
|||
|
|
@ -131,7 +131,4 @@ def test_invalid_path():
|
|||
with pytest.raises(ValueError, match="Path does not exist"):
|
||||
list_directory_tree.invoke({"path": "/nonexistent/path"})
|
||||
|
||||
with pytest.raises(ValueError, match="Path is not a directory"):
|
||||
list_directory_tree.invoke(
|
||||
{"path": __file__}
|
||||
) # Try to list the test file itself
|
||||
# We now allow files to be passed to list_directory_tree, so we don't test for this case anymore
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
"""Test for list_directory_tree with file path support."""
|
||||
|
||||
import tempfile
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from ra_aid.tools import list_directory_tree
|
||||
|
||||
|
||||
def test_list_directory_tree_with_file():
|
||||
"""Test that list_directory_tree works with a file path."""
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
||||
tmp_file.write(b"Some test content")
|
||||
tmp_file_path = tmp_file.name
|
||||
|
||||
try:
|
||||
# Test with file path
|
||||
result = list_directory_tree.invoke({"path": tmp_file_path})
|
||||
|
||||
# Basic verification that the output contains the filename
|
||||
filename = os.path.basename(tmp_file_path)
|
||||
assert filename in result
|
||||
|
||||
# Test with size option
|
||||
result_with_size = list_directory_tree.invoke({"path": tmp_file_path, "show_size": True})
|
||||
assert "(" in result_with_size # Size information should be present
|
||||
|
||||
# Test with modified time option
|
||||
result_with_time = list_directory_tree.invoke({"path": tmp_file_path, "show_modified": True})
|
||||
assert "Modified:" in result_with_time
|
||||
finally:
|
||||
# Clean up the temporary file
|
||||
if os.path.exists(tmp_file_path):
|
||||
os.unlink(tmp_file_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_list_directory_tree_with_file()
|
||||
print("All tests passed!")
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
"""Test file listing for non-git directories."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from ra_aid.file_listing import get_file_listing
|
||||
|
||||
|
||||
def test_non_git_file_listing():
|
||||
"""Test that file listing works correctly for non-git directories."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
# Create a few test files
|
||||
file1 = Path(temp_dir) / "file1.txt"
|
||||
file2 = Path(temp_dir) / "file2.py"
|
||||
file3 = Path(temp_dir) / ".hidden_file" # Hidden file
|
||||
|
||||
# Create a subdirectory with files
|
||||
subdir = Path(temp_dir) / "subdir"
|
||||
os.makedirs(subdir)
|
||||
file4 = subdir / "file4.js"
|
||||
|
||||
# Create excluded directories
|
||||
ra_aid_dir = Path(temp_dir) / ".ra-aid"
|
||||
venv_dir = Path(temp_dir) / ".venv"
|
||||
os.makedirs(ra_aid_dir)
|
||||
os.makedirs(venv_dir)
|
||||
|
||||
# Create files in excluded directories
|
||||
ra_aid_file = ra_aid_dir / "config.json"
|
||||
venv_file = venv_dir / "activate"
|
||||
|
||||
# Write content to all files
|
||||
for file_path in [file1, file2, file3, file4, ra_aid_file, venv_file]:
|
||||
with open(file_path, "w") as f:
|
||||
f.write("test content")
|
||||
|
||||
# Test regular file listing (should exclude hidden files and directories)
|
||||
files, count = get_file_listing(temp_dir)
|
||||
assert count == 3 # file1.txt, file2.py, subdir/file4.js
|
||||
assert set(files) == {"file1.txt", "file2.py", os.path.join("subdir", "file4.js")}
|
||||
|
||||
# Test with include_hidden=True
|
||||
files_with_hidden, count_with_hidden = get_file_listing(temp_dir, include_hidden=True)
|
||||
assert count_with_hidden == 4 # Including .hidden_file
|
||||
assert ".hidden_file" in files_with_hidden
|
||||
|
||||
# Test with limit
|
||||
files_limited, count_limited = get_file_listing(temp_dir, limit=2)
|
||||
assert len(files_limited) == 2
|
||||
assert count_limited == 3 # Total count should still be 3
|
||||
|
||||
|
||||
def test_non_git_directory_with_excluded_dirs():
|
||||
"""Test that excluded directories are properly handled in non-git directories."""
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
# Create regular files
|
||||
file1 = Path(temp_dir) / "file1.txt"
|
||||
with open(file1, "w") as f:
|
||||
f.write("test content")
|
||||
|
||||
# Create excluded directories with files
|
||||
excluded_dirs = [".ra-aid", ".venv", ".git", ".aider", "__pycache__"]
|
||||
for excluded_dir in excluded_dirs:
|
||||
dir_path = Path(temp_dir) / excluded_dir
|
||||
os.makedirs(dir_path)
|
||||
with open(dir_path / "test_file.txt", "w") as f:
|
||||
f.write("test content")
|
||||
|
||||
# Get file listing
|
||||
files, count = get_file_listing(temp_dir)
|
||||
|
||||
# Should only include the regular file
|
||||
assert count == 1
|
||||
assert files == ["file1.txt"]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main(["-xvs", __file__])
|
||||
Loading…
Reference in New Issue