Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions dare_framework/tool/_internal/tools/search_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def name(self) -> str:

@property
def description(self) -> str:
return "Search file paths by glob pattern (e.g. *.py, src/**/*.ts). Returns paths as absolute paths. Use paths directly for read_file."
return "Search file paths by glob pattern (e.g. *.py, src/**/*.ts). Returns workspace-relative paths."

@property
def input_schema(self) -> dict[str, Any]:
Expand All @@ -57,7 +57,7 @@ def output_schema(self) -> dict[str, Any]:
return {
"type": "object",
"properties": {
"paths": {"type": "array", "items": {"type": "string"}, "description": "Absolute paths; use directly for read_file"},
"paths": {"type": "array", "items": {"type": "string"}, "description": "Workspace-relative paths"},
"total_matches": {"type": "integer"},
"truncated": {"type": "boolean"},
},
Expand Down Expand Up @@ -145,7 +145,7 @@ def _execute_search_file(input: dict[str, Any], context: RunContext[Any]) -> Too
abs_path = search_path.resolve()
rel_path = _normalized_relative_path(abs_path, root)
if _match_pattern(pattern, rel_path):
matches.append(str(abs_path).replace("\\", "/"))
matches.append(rel_path)
else:
for dirpath, dirs, files in os.walk(search_path, topdown=True, followlinks=False):
dirs[:] = [d for d in sorted(dirs) if d not in ignore_dirs]
Expand All @@ -154,7 +154,7 @@ def _execute_search_file(input: dict[str, Any], context: RunContext[Any]) -> Too
rel_path = _normalized_relative_path(abs_path, root)
if not _match_pattern(pattern, rel_path):
continue
matches.append(str(abs_path).replace("\\", "/"))
matches.append(rel_path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve root context in returned search_file paths

Appending rel_path here drops which workspace root a match came from, which breaks multi-root setups introduced by this change from absolute paths. If search_file is run against a non-primary root (for example by passing an absolute path in the second workspace root), the returned relative name is later resolved by read_file via resolve_path against roots[0], so the same path can resolve to the wrong file or fail with FILE_NOT_FOUND even though search_file reported it as a match.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ce9f26b by preserving root context in search outputs and teaching path resolution to consume it.

What changed:

  • search_file now prefixes non-primary workspace roots as @root[n]/... (primary root remains unchanged).
  • resolve_path now parses @root[n]/... and resolves against the selected workspace root.
  • Added regression tests for both behaviors:
    • test_search_file_prefixes_secondary_root_paths
    • test_read_file_accepts_secondary_root_prefixed_path

Verification:

  • .venv/bin/pytest -q tests/unit/test_v4_file_tools.py::test_search_file_prefixes_secondary_root_paths tests/unit/test_v4_file_tools.py::test_read_file_accepts_secondary_root_prefixed_path
  • .venv/bin/pytest -q tests/unit/test_v4_file_tools.py

if len(matches) >= max_results:
truncated = True
break
Expand Down Expand Up @@ -200,4 +200,3 @@ def _error_result(error: ToolError) -> ToolResult:
error=error.message,
evidence=[],
)