fix(cli): align tool list rich output with spec (Read-Only/Writes columns and Summary panel)
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Failing after 48s
CI / build (pull_request) Successful in 33s
CI / quality (pull_request) Successful in 1m2s
CI / helm (pull_request) Successful in 43s
CI / security (pull_request) Failing after 1m17s
CI / typecheck (pull_request) Failing after 1m19s
CI / coverage (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped
CI / unit_tests (pull_request) Failing after 2m10s
CI / docker (pull_request) Has been skipped
CI / e2e_tests (pull_request) Failing after 15m48s
CI / integration_tests (pull_request) Failing after 18m33s
CI / status-check (pull_request) Failing after 3s

Replaced the broken PR implementation with a complete spec-compliant fix:

- Removed Description and Timeout columns (not in spec)
- Reordered columns to spec order: Name, Type, Source, Read-Only, Writes
- Fixed column/row count mismatch: add_row now passes exactly 5 values
- Added capability-based Read-Only/Writes computation (read_only/writes fields)
- Renders checkmark (tick) or em dash based on capability metadata
- Added Summary panel with Total, Tools, Validations, Read-Only, Writes, Namespaces counts
- Added OK status message listing tool count

ISSUES CLOSED: #1476
This commit is contained in:
2026-04-29 17:22:22 +00:00
parent 31aa63e650
commit c1546e1eaf
+65 -19
View File
@@ -38,7 +38,7 @@ code: |
|----------------------------|------------------------------------------|
| ``Config file error`` | File not found or not readable |
| ``Schema validation error``| YAML does not match Tool schema |
| ``Duplicate tool`` | Tool name already registered |
| ``Duplicate tool`` | Tool name already registered |
Based on implementation_plan.md -- Task C1.tool.cli.
"""
@@ -244,12 +244,12 @@ def add(
# Handle spec-compliant 'tool:' wrapper key format
# If the YAML has a top-level 'tool:' key, extract its contents
if isinstance(config_dict, dict) and 'tool' in config_dict:
config_dict = config_dict['tool']
if isinstance(config_dict, dict) and "tool" in config_dict:
config_dict = config_dict["tool"]
# Ignore 'cleveragents:' version header if present
if isinstance(config_dict, dict) and 'cleveragents' in config_dict:
del config_dict['cleveragents']
if isinstance(config_dict, dict) and "cleveragents" in config_dict:
del config_dict["cleveragents"]
if not isinstance(config_dict, dict):
raise ValueError("YAML config must be a mapping")
@@ -413,31 +413,77 @@ def list_tools(
console.print(format_output(data, fmt))
return
# Rich table
# Rich table — spec-compliant: Name, Type, Source, Read-Only, Writes
table = Table(title=f"Tools ({len(tools)} total)")
table.add_column("Name", style="cyan")
table.add_column("Read-Only", style="cyan")
table.add_column("Writes", style="yellow")
table.add_column("Type", style="blue")
table.add_column("Source", style="magenta")
table.add_column("Description", style="dim")
table.add_column("Timeout", justify="right")
table.add_column("Read-Only", justify="center", style="green")
table.add_column("Writes", justify="center", style="yellow")
read_only_count: int = 0
writes_count: int = 0
tool_count: int = 0
validation_count: int = 0
namespaces: set[str] = set()
for tool in tools:
spec = _tool_spec_dict(tool)
desc = str(spec.get("description", ""))
if len(desc) > 40:
desc = desc[:37] + "..."
name_val = str(spec.get("name", ""))
tool_type_val = str(spec.get("tool_type", "tool"))
source_val = str(spec.get("source", ""))
# Determine Read-Only / Writes from capability metadata
capability = spec.get("capability", {})
if isinstance(capability, dict):
is_read_only: bool = capability.get("read_only", False)
has_writes: bool = capability.get("writes", False)
else:
is_read_only = False
has_writes = False
read_only_str: str = "\u2713" if is_read_only else "\u2014"
writes_str: str = "\u2713" if has_writes else "\u2014"
table.add_row(
str(spec.get("name", "")),
str(spec.get("tool_type", "tool")),
str(spec.get("source", "")),
desc,
str(spec.get("timeout", 300)),
name_val,
tool_type_val,
source_val,
read_only_str,
writes_str,
)
# Accumulate summary statistics
namespaces.add(
name_val.split("/", 1)[0] if "/" in name_val else name_val
)
if tool_type_val == "validation":
validation_count += 1
else:
tool_count += 1
if is_read_only:
read_only_count += 1
if has_writes:
writes_count += 1
console.print(table)
# Summary panel
summary_lines = [
f" Total: {len(tools)}",
f" Tools: {tool_count}",
f" Validations: {validation_count}",
f" Read-Only: {read_only_count}",
f" Writes: {writes_count}",
f" Namespaces: {len(namespaces)}",
]
summary_panel = Panel(
"\n".join(summary_lines),
title="Summary",
expand=False,
)
console.print(summary_panel)
console.print(f"\n\u2713 OK {len(tools)} tools listed\n")
except CleverAgentsError as exc:
console.print(f"[red]Error:[/red] {exc.message}")
raise typer.Abort() from exc