From 8d508f3d53f70afb57726277059db5b49ea77f18 Mon Sep 17 00:00:00 2001 From: Birfy <15378933+Birfy@users.noreply.github.com> Date: Sun, 8 Nov 2026 06:04:01 +0800 Subject: [PATCH] fix(algorithms): suggest the near-miss card for an unknown name (#874) --- agentdescent/algorithms/__init__.py | 6 ++++++ changelog.d/874-algorithm-did-you-mean.md | 4 ++++ tests/test_algorithms.py | 13 +++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 changelog.d/874-algorithm-did-you-mean.md diff --git a/agentdescent/algorithms/__init__.py b/agentdescent/algorithms/__init__.py index 1192f89f..13053bb1 100644 --- a/agentdescent/algorithms/__init__.py +++ b/agentdescent/algorithms/__init__.py @@ -321,6 +321,12 @@ def _unknown_algorithm_message(where: str, name: Any) -> str: return msg slug = _fold(name) if slug not in _EXAMPLES_ONLY_PORTS: + # A near-miss typo (`gepaa`, `evo_skill`) names the card it most + # likely meant, so the fix is one word rather than a list scan. + import difflib + close = difflib.get_close_matches(slug, sorted(_REGISTRY), n=1, cutoff=0.75) + if close: + return f"{msg}; did you mean {close[0]!r}?" return msg doc = f"docs/algo-{slug.replace('_', '-')}.md" return (f"{msg}. {slug!r} is an examples/ research port ({doc}), not a " diff --git a/changelog.d/874-algorithm-did-you-mean.md b/changelog.d/874-algorithm-did-you-mean.md new file mode 100644 index 00000000..819e998c --- /dev/null +++ b/changelog.d/874-algorithm-did-you-mean.md @@ -0,0 +1,4 @@ +Changed +An unknown algorithm name that is a near-miss typo of a registered card now ends +with "did you mean 'gepa'?" (`algorithms show` / `run`, specs, MCP), instead of +only listing every card (#874). diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py index e8b56d5a..c7001608 100644 --- a/tests/test_algorithms.py +++ b/tests/test_algorithms.py @@ -1060,3 +1060,16 @@ def test_mcp_list_algorithms_carries_cards_vs_ports_note(): names = {a["name"] for a in payload["algorithms"]} assert "gepa" in names and "evoskill" not in names # kind filter assert all(a["surface"] == "card" for a in payload["algorithms"]) + + +@pytest.mark.parametrize("typo,meant", [("gepaa", "gepa"), ("evo_skill", "evoskill"), + ("skilopt", "skillopt")]) +def test_unknown_algorithm_names_the_near_miss(typo, meant): + with pytest.raises(AlgorithmError, match=rf"did you mean '{meant}'\?$"): + algorithms.get(typo, where="show") + + +def test_unknown_algorithm_without_near_miss_has_no_guess(): + with pytest.raises(AlgorithmError) as e: + algorithms.get("nope", where="show") + assert "did you mean" not in str(e.value)