mirror of
https://github.com/NCBM/plyngent.git
synced 2026-07-23 05:55:16 +08:00
core/cli: fix /help command usage line and help Tab completion
Use a standalone Click context so /help compact shows Usage: /compact, not nested under help. Complete /help <cmd> from the slash registry.
This commit is contained in:
@@ -60,17 +60,20 @@ def build_completer(state: ReplState) -> Callable[[str, int], str | None]:
|
|||||||
|
|
||||||
|
|
||||||
def _argument_options(state: ReplState, command: str, text: str) -> list[str]:
|
def _argument_options(state: ReplState, command: str, text: str) -> list[str]:
|
||||||
if command == "/provider":
|
candidates: list[str] = []
|
||||||
return filter_prefix(text, sorted(state.config.providers.keys()))
|
if command == "/help":
|
||||||
if command == "/model":
|
# Second token: command name without leading slash (also accept /name).
|
||||||
return filter_prefix(text, sorted(state.provider.models.keys()))
|
candidates = [n.removeprefix("/") for n in slash_commands()]
|
||||||
if command in {"/tools", "/stream", "/verbose"}:
|
text = text.lstrip("/")
|
||||||
return filter_prefix(text, list(_ON_OFF_ARGS))
|
elif command == "/provider":
|
||||||
if command == "/export":
|
candidates = sorted(state.config.providers.keys())
|
||||||
return filter_prefix(text, list(_EXPORT_ARGS))
|
elif command == "/model":
|
||||||
if command == "/resume":
|
candidates = sorted(state.provider.models.keys())
|
||||||
return []
|
elif command in {"/tools", "/stream", "/verbose"}:
|
||||||
return []
|
candidates = list(_ON_OFF_ARGS)
|
||||||
|
elif command == "/export":
|
||||||
|
candidates = list(_EXPORT_ARGS)
|
||||||
|
return filter_prefix(text, candidates)
|
||||||
|
|
||||||
|
|
||||||
def bind_tab_complete(readline_mod: object) -> None:
|
def bind_tab_complete(readline_mod: object) -> None:
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ def help_cmd(ctx: click.Context, command: str | None) -> None:
|
|||||||
if cmd is None:
|
if cmd is None:
|
||||||
click.echo(f"unknown command /{name}; try /help")
|
click.echo(f"unknown command /{name}; try /help")
|
||||||
return
|
return
|
||||||
# Build a child context so get_help includes usage.
|
# Standalone context (no parent=help) so usage is "/compact …" not "help … compact".
|
||||||
with click.Context(cmd, info_name=name, parent=ctx) as sub:
|
with click.Context(cmd, info_name=f"/{name}") as sub:
|
||||||
click.echo(cmd.get_help(sub))
|
click.echo(cmd.get_help(sub))
|
||||||
return
|
return
|
||||||
click.echo(slash.get_help(ctx))
|
click.echo(slash.get_help(ctx))
|
||||||
|
|||||||
@@ -116,3 +116,29 @@ def test_completer_provider_args(tmp_path: object, monkeypatch: object) -> None:
|
|||||||
|
|
||||||
first = completer("r", 0)
|
first = completer("r", 0)
|
||||||
assert first == "remote"
|
assert first == "remote"
|
||||||
|
|
||||||
|
|
||||||
|
def test_completer_help_commands(tmp_path: object, monkeypatch: object) -> None:
|
||||||
|
import readline
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
assert isinstance(tmp_path, Path)
|
||||||
|
assert isinstance(monkeypatch, pytest.MonkeyPatch)
|
||||||
|
|
||||||
|
state = _minimal_state(tmp_path)
|
||||||
|
completer = build_completer(state)
|
||||||
|
monkeypatch.setattr(readline, "get_line_buffer", lambda: "/help ")
|
||||||
|
monkeypatch.setattr(readline, "get_begidx", lambda: len("/help "))
|
||||||
|
|
||||||
|
found: list[str] = []
|
||||||
|
index = 0
|
||||||
|
while True:
|
||||||
|
item = completer("c", index)
|
||||||
|
if item is None:
|
||||||
|
break
|
||||||
|
found.append(item)
|
||||||
|
index += 1
|
||||||
|
assert "compact" in found
|
||||||
|
assert "clear" in found
|
||||||
|
|||||||
@@ -96,6 +96,14 @@ async def test_help_and_clear(state: ReplState) -> None:
|
|||||||
assert state.agent.messages == []
|
assert state.agent.messages == []
|
||||||
|
|
||||||
|
|
||||||
|
async def test_help_command_usage_line(state: ReplState, capsys: pytest.CaptureFixture[str]) -> None:
|
||||||
|
assert await handle_slash(state, "/help compact") is True
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "Usage: /compact" in out
|
||||||
|
assert "help [COMMAND] compact" not in out
|
||||||
|
assert "Soft-compact" in out
|
||||||
|
|
||||||
|
|
||||||
async def test_quit(state: ReplState) -> None:
|
async def test_quit(state: ReplState) -> None:
|
||||||
assert await handle_slash(state, "/quit") is False
|
assert await handle_slash(state, "/quit") is False
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user