Skip to content

Commit 318f772

Browse files
Kasper JungeRalphify
authored andcommitted
refactor: consolidate tool styles and arg extractors into single registry
The tool display config was split across two independent dicts (_TOOL_STYLES and _TOOL_ARG_EXTRACTORS) that could drift out of sync — Agent and ToolSearch had arg extractors but no visual styles. Merge both into a unified _TOOL_REGISTRY with a _ToolConfig class so adding a new tool is a single-line change. Also adds missing styles for Agent (violet/agent) and ToolSearch (blue/other). Co-authored-by: Ralphify <noreply@ralphify.co>
1 parent 43bd2e2 commit 318f772

1 file changed

Lines changed: 47 additions & 40 deletions

File tree

src/ralphify/_console_emitter.py

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -157,28 +157,11 @@ def _extract_file_path(i: dict[str, Any]) -> str:
157157
return _shorten_path(i.get("file_path", ""))
158158

159159

160-
_TOOL_ARG_EXTRACTORS: dict[str, Callable[[dict[str, Any]], str]] = {
161-
"Read": _extract_file_path,
162-
"Write": _extract_file_path,
163-
"Edit": _extract_file_path,
164-
"MultiEdit": _extract_file_path,
165-
"Glob": lambda i: i.get("pattern", ""),
166-
"Grep": lambda i: i.get("pattern", ""),
167-
"Bash": lambda i: i.get("command", ""),
168-
"Task": lambda i: _format_params(i, ["description", "prompt"]),
169-
"WebFetch": lambda i: i.get("url", ""),
170-
"WebSearch": lambda i: i.get("query", ""),
171-
"TodoWrite": lambda i: f"{len(i.get('todos', []))} todos",
172-
"Agent": lambda i: _format_params(i, ["description", "prompt"]),
173-
"ToolSearch": lambda i: _format_params(i, ["query", "max_results"]),
174-
}
175-
176-
177160
def _extract_tool_arg(name: str, tool_input: dict[str, Any]) -> str:
178161
"""Return the most relevant argument string for a tool call."""
179-
extractor = _TOOL_ARG_EXTRACTORS.get(name)
180-
if extractor is not None:
181-
return extractor(tool_input)
162+
cfg = _TOOL_REGISTRY.get(name)
163+
if cfg is not None and cfg.extract_arg is not None:
164+
return cfg.extract_arg(tool_input)
182165
return ", ".join(sorted(tool_input.keys()))
183166

184167

@@ -227,25 +210,48 @@ def _format_run_info(
227210
return " · ".join(parts)
228211

229212

230-
# ── Iteration panel ──────────────────────────────────────────────────
231-
232-
# (color, category) for each known tool. Color is applied to the tool
233-
# name in scroll lines so the activity feed scans visually by intent —
234-
# blue=read/search, orange=mutate, green=execute, lavender=web, etc.
235-
# Category is the bucket used in the footer's compact tool counter.
236-
_TOOL_STYLES: dict[str, tuple[str, str]] = {
237-
"Read": (_brand.BLUE, "read"),
238-
"Glob": (_brand.BLUE, "glob"),
239-
"Grep": (_brand.BLUE, "grep"),
240-
"Edit": (_brand.ORANGE, "edit"),
241-
"MultiEdit": (_brand.ORANGE, "edit"),
242-
"Write": (_brand.ORANGE, "write"),
243-
"Bash": (_brand.GREEN, "bash"),
244-
"BashOutput": (_brand.GREEN, "bash"),
245-
"WebFetch": (_brand.LAVENDER, "web"),
246-
"WebSearch": (_brand.LAVENDER, "web"),
247-
"Task": (_brand.VIOLET, "task"),
248-
"TodoWrite": (_brand.PURPLE, "todo"),
213+
# ── Tool registry ───────────────────────────────────────────────────
214+
#
215+
# Single source of truth for tool display config in the activity feed.
216+
# Each entry defines the color (applied to the tool name), the category
217+
# bucket (shown in the footer counter), and an optional argument
218+
# extractor (picks the most relevant arg for the scroll line).
219+
#
220+
# Color intent: blue=read/search, orange=mutate, green=execute,
221+
# lavender=web, violet=delegate, purple=meta.
222+
223+
224+
class _ToolConfig:
225+
"""Visual and extraction config for a known tool."""
226+
227+
__slots__ = ("color", "category", "extract_arg")
228+
229+
def __init__(
230+
self,
231+
color: str,
232+
category: str,
233+
extract_arg: Callable[[dict[str, Any]], str] | None = None,
234+
) -> None:
235+
self.color = color
236+
self.category = category
237+
self.extract_arg = extract_arg
238+
239+
240+
_TOOL_REGISTRY: dict[str, _ToolConfig] = {
241+
"Read": _ToolConfig(_brand.BLUE, "read", _extract_file_path),
242+
"Glob": _ToolConfig(_brand.BLUE, "glob", lambda i: i.get("pattern", "")),
243+
"Grep": _ToolConfig(_brand.BLUE, "grep", lambda i: i.get("pattern", "")),
244+
"Edit": _ToolConfig(_brand.ORANGE, "edit", _extract_file_path),
245+
"MultiEdit": _ToolConfig(_brand.ORANGE, "edit", _extract_file_path),
246+
"Write": _ToolConfig(_brand.ORANGE, "write", _extract_file_path),
247+
"Bash": _ToolConfig(_brand.GREEN, "bash", lambda i: i.get("command", "")),
248+
"BashOutput": _ToolConfig(_brand.GREEN, "bash"),
249+
"WebFetch": _ToolConfig(_brand.LAVENDER, "web", lambda i: i.get("url", "")),
250+
"WebSearch": _ToolConfig(_brand.LAVENDER, "web", lambda i: i.get("query", "")),
251+
"Task": _ToolConfig(_brand.VIOLET, "task", lambda i: _format_params(i, ["description", "prompt"])),
252+
"Agent": _ToolConfig(_brand.VIOLET, "agent", lambda i: _format_params(i, ["description", "prompt"])),
253+
"ToolSearch": _ToolConfig(_brand.BLUE, "other", lambda i: _format_params(i, ["query", "max_results"])),
254+
"TodoWrite": _ToolConfig(_brand.PURPLE, "todo", lambda i: f"{len(i.get('todos', []))} todos"),
249255
}
250256

251257
_DEFAULT_TOOL_STYLE: tuple[str, str] = ("white", "other")
@@ -257,7 +263,8 @@ def _format_run_info(
257263

258264

259265
def _tool_style_for(name: str) -> tuple[str, str]:
260-
return _TOOL_STYLES.get(name, _DEFAULT_TOOL_STYLE)
266+
cfg = _TOOL_REGISTRY.get(name)
267+
return (cfg.color, cfg.category) if cfg is not None else _DEFAULT_TOOL_STYLE
261268

262269

263270
class _LivePanelBase:

0 commit comments

Comments
 (0)