core/cli: /tools --list shows tags and catalog source

This commit is contained in:
2026-07-24 15:04:33 +08:00
parent 7253ca84dd
commit 662e95f693
2 changed files with 66 additions and 1 deletions
+49 -1
View File
@@ -908,14 +908,62 @@ def model_cmd(state: ReplState, model_id: str | None, *, persist: bool) -> None:
click.secho(f"error: could not persist model: {exc}", fg="red") click.secho(f"error: could not persist model: {exc}", fg="red")
def _format_tool_tags(tags: object) -> str:
"""Compact Flag names for operator display (e.g. LOCAL|INSTANCE_STATE)."""
from plyngent.agent.tools import ToolTag
if not isinstance(tags, ToolTag):
return str(tags)
if tags.value == 0:
return "-"
names = [member.name for member in ToolTag if member.name and (tags & member) == member]
return "|".join(names) if names else str(tags)
@slash.command("tools") @slash.command("tools")
@click.argument("enabled", required=False, type=ON_OFF, metavar="[on|off]") @click.argument("enabled", required=False, type=ON_OFF, metavar="[on|off]")
@click.option(
"--list",
"list_tools",
is_flag=True,
default=False,
help="List registry tools with tags and catalog source.",
)
@click.pass_obj @click.pass_obj
def tools_cmd(state: ReplState, enabled: bool | None) -> None: # noqa: FBT001 def tools_cmd(
state: ReplState,
enabled: bool | None, # noqa: FBT001
*,
list_tools: bool,
) -> None:
"""Show or set whether agent tools are enabled. """Show or set whether agent tools are enabled.
Omit the argument to print the current value; pass ``on`` or ``off`` to change it. Omit the argument to print the current value; pass ``on`` or ``off`` to change it.
Use ``--list`` to print name, tags, and catalog source for each registry tool.
""" """
if list_tools:
if not state.tools_enabled or state.agent.tools is None:
click.echo("tools=off (no registry)")
return
from plyngent.tools.catalog import get_catalog
catalog = get_catalog()
registry = state.agent.tools
# ToolRegistry has no name iterator; use schema items then re-resolve defs.
items = sorted(registry.tool_items(), key=lambda item: item.function.name)
click.echo(f"tools=on count={len(items)}")
for item in items:
name = item.function.name
definition = registry.get(name)
tags_s = _format_tool_tags(definition.tags) if definition is not None else "-"
entry = catalog.get(name)
source_s = str(entry.source) if entry is not None else "registry"
click.echo(f" {name}\t{tags_s}\t{source_s}")
if enabled is not None:
state.tools_enabled = enabled
state.rebuild_client()
click.echo(f"tools={'on' if enabled else 'off'}")
return
if enabled is None: if enabled is None:
click.echo(f"tools={'on' if state.tools_enabled else 'off'}") click.echo(f"tools={'on' if state.tools_enabled else 'off'}")
return return
+17
View File
@@ -166,6 +166,23 @@ async def test_tools_toggle(state: ReplState) -> None:
assert state.tools_enabled is False assert state.tools_enabled is False
async def test_tools_list(state: ReplState, capsys: pytest.CaptureFixture[str]) -> None:
assert await handle_slash(state, "/tools --list") is True
out_off = capsys.readouterr().out
assert "tools=off" in out_off or "no registry" in out_off
assert await handle_slash(state, "/tools on") is True
assert state.tools_enabled is True
assert state.agent.tools is not None
_ = capsys.readouterr()
assert await handle_slash(state, "/tools --list") is True
out = capsys.readouterr().out
assert "tools=on count=" in out
assert "read_file" in out
assert "LOCAL" in out
assert "builtin" in out
async def test_rename_slash(state: ReplState) -> None: async def test_rename_slash(state: ReplState) -> None:
sid = state.session_id sid = state.session_id
assert sid is not None assert sid is not None