diff --git a/clients/terminal/tui/renderers/filesystem.py b/clients/terminal/tui/renderers/filesystem.py
index abb5b24..d7dbfc1 100644
--- a/clients/terminal/tui/renderers/filesystem.py
+++ b/clients/terminal/tui/renderers/filesystem.py
@@ -87,8 +87,12 @@
return self._render_read(text, args, theme)
if action == "info":
return self._render_info(text, theme)
- # write / edit / append / move / copy / delete / mkdir / exists / find /
- # find_up / query / unknown → plain, unchanged.
+ if action == "find":
+ return self._render_find(text, theme)
+ if action == "find_up":
+ return self._render_find_up(text, theme)
+ # write / edit / append / move / copy / delete / mkdir / exists / query /
+ # unknown → plain, unchanged.
return Text(text, style=theme.text_dim.hex)
# ── diff ───────────────────────────────────────────────────────────────
@@ -180,6 +184,49 @@
out.append(line, style=theme.text.hex)
return out
+ # ── find ───────────────────────────────────────────────────────────────
+
+ def _render_find(self, text: str, theme: Theme) -> RenderableType:
+ lines = text.splitlines()
+ if not lines:
+ return Text(text, style=theme.text_dim.hex)
+
+ out = Text()
+ for idx, line in enumerate(lines):
+ if idx:
+ out.append("\n")
+ if idx == 0 and line.startswith("["):
+ style = (
+ theme.warning.hex
+ if _TRUNCATED_MARKER in line
+ else theme.text_dim.hex
+ )
+ out.append(line, style=style)
+ continue
+ if line.startswith("No matches for "):
+ out.append(line, style=theme.text_dim.hex)
+ continue
+ # Match line: "{path} ({size})" or "
" instead of size.
+ path_part, sep, tail = line.rpartition(" (")
+ if sep:
+ out.append(path_part, style=theme.text.hex)
+ out.append(" (", style=theme.text_dim.hex)
+ if tail == ")":
+ out.append("", style=theme.info.hex)
+ out.append(")", style=theme.text_dim.hex)
+ else:
+ out.append(tail, style=theme.text_dim.hex)
+ else:
+ out.append(line, style=theme.text.hex)
+ return out
+
+ # ── find_up ────────────────────────────────────────────────────────────
+
+ def _render_find_up(self, text: str, theme: Theme) -> RenderableType:
+ if text.startswith("not found (searched:"):
+ return Text(text, style=theme.text_dim.hex)
+ return Text(text, style=theme.accent.hex)
+
# ── list ───────────────────────────────────────────────────────────────
def _render_list(self, text: str, theme: Theme) -> RenderableType:
diff --git a/tests/clients/test_filesystem_renderer.py b/tests/clients/test_filesystem_renderer.py
index 193b7cd..6de92cc 100644
--- a/tests/clients/test_filesystem_renderer.py
+++ b/tests/clients/test_filesystem_renderer.py
@@ -270,6 +270,102 @@
assert "a.py:1: x y" in body.plain
+# ── find ──────────────────────────────────────────────────────────────────────
+
+
+def test_find_highlights_header_and_match_lines() -> None:
+ set_active_theme("gnexus-dark")
+ theme = get_active_theme()
+ renderer = FilesystemToolResultRenderer()
+ msg = {
+ "type": "tool_call",
+ "tool": "filesystem",
+ "args": {"action": "find", "pattern": "*.py"},
+ "result": (
+ "[2 matches for '*.py' in /proj]\n"
+ "/proj/a.py (1.2K)\n"
+ "/proj/src ()\n"
+ ),
+ "success": True,
+ }
+ body = _body(renderer.render(msg))
+ assert isinstance(body, Text)
+ # Header plaque is dim.
+ assert theme.text_dim.hex in _span_styles_at(body, "2 matches")
+ # Match path in text, size in dim.
+ assert theme.text.hex in _span_styles_at(body, "/proj/a.py")
+ assert theme.text_dim.hex in _span_styles_at(body, "1.2K")
+ # "" marker in info color.
+ assert theme.info.hex in _span_styles_at(body, "")
+
+
+def test_find_truncated_header_uses_warning_color() -> None:
+ set_active_theme("gnexus-dark")
+ theme = get_active_theme()
+ renderer = FilesystemToolResultRenderer()
+ msg = {
+ "type": "tool_call",
+ "tool": "filesystem",
+ "args": {"action": "find", "pattern": "*"},
+ "result": "[200 matches for '*' in /proj ⚠ showing first 200]\n/p/a (0B)\n",
+ "success": True,
+ }
+ body = _body(renderer.render(msg))
+ assert isinstance(body, Text)
+ assert theme.warning.hex in _span_styles_at(body, "showing first 200")
+
+
+def test_find_no_matches_renders_dim() -> None:
+ set_active_theme("gnexus-dark")
+ theme = get_active_theme()
+ renderer = FilesystemToolResultRenderer()
+ msg = {
+ "type": "tool_call",
+ "tool": "filesystem",
+ "args": {"action": "find", "pattern": "*.x"},
+ "result": "No matches for '*.x' in /proj",
+ "success": True,
+ }
+ body = _body(renderer.render(msg))
+ assert isinstance(body, Text)
+ assert _span_styles_at(body, "No matches") == {theme.text_dim.hex}
+
+
+# ── find_up ───────────────────────────────────────────────────────────────────
+
+
+def test_find_up_found_renders_path_in_accent() -> None:
+ set_active_theme("gnexus-dark")
+ theme = get_active_theme()
+ renderer = FilesystemToolResultRenderer()
+ msg = {
+ "type": "tool_call",
+ "tool": "filesystem",
+ "args": {"action": "find_up", "pattern": "pyproject.toml"},
+ "result": "/proj/pyproject.toml",
+ "success": True,
+ }
+ body = _body(renderer.render(msg))
+ assert isinstance(body, Text)
+ assert _span_styles_at(body, "/proj/pyproject.toml") == {theme.accent.hex}
+
+
+def test_find_up_not_found_renders_dim() -> None:
+ set_active_theme("gnexus-dark")
+ theme = get_active_theme()
+ renderer = FilesystemToolResultRenderer()
+ msg = {
+ "type": "tool_call",
+ "tool": "filesystem",
+ "args": {"action": "find_up", "pattern": "x.txt"},
+ "result": "not found (searched: /a/x.txt, /x.txt)",
+ "success": True,
+ }
+ body = _body(renderer.render(msg))
+ assert isinstance(body, Text)
+ assert _span_styles_at(body, "not found") == {theme.text_dim.hex}
+
+
# ── fallback ──────────────────────────────────────────────────────────────────