fix: word-boundary fuzzy alias search, _resolve_alias in remove_server

- "tor" now matches "API TOR contabo..." but NOT "investor"
- Whole-word match first, substring fallback second
- remove_server() now uses _resolve_alias() for consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chrome-storm-c442
2026-02-24 07:02:38 -05:00
parent afa3210260
commit 8554c73ba0

View File

@@ -250,12 +250,39 @@ def list_servers(full=False):
print(f"{alias:<20} {stype:<10} {has_key:<6} {notes}")
def _resolve_alias(alias: str, servers: dict) -> str:
"""Resolve alias — exact match, then whole-word search, then substring fallback."""
if alias in servers:
return alias
query = alias.lower()
# 1) Whole-word match (e.g. "tor" matches "API TOR contabo" but NOT "investor")
import re
word_re = re.compile(r'\b' + re.escape(query) + r'\b', re.IGNORECASE)
word_matches = [a for a in servers if word_re.search(a)]
if len(word_matches) == 1:
return word_matches[0]
if len(word_matches) > 1:
print(f"Ambiguous: '{alias}' matches multiple servers:")
for m in word_matches:
print(f" - {m}")
sys.exit(1)
# 2) Substring fallback (e.g. "cont" matches "contabo")
sub_matches = [a for a in servers if query in a.lower()]
if len(sub_matches) == 1:
return sub_matches[0]
if len(sub_matches) > 1:
print(f"Ambiguous: '{alias}' matches multiple servers:")
for m in sub_matches:
print(f" - {m}")
sys.exit(1)
print(f"Unknown: '{alias}'. Available: {', '.join(servers.keys())}")
sys.exit(1)
def server_info(alias: str):
"""Show server info safe for AI context — NO ip, user, password, port, totp_secret."""
_, servers = load_servers()
if alias not in servers:
print(f"Unknown: {alias}. Available: {', '.join(servers.keys())}")
sys.exit(1)
alias = _resolve_alias(alias, servers)
s = servers[alias]
has_key = "yes" if os.path.exists(SSH_KEY_PATH) else "no"
print(f"Alias: {s['alias']}")
@@ -318,9 +345,7 @@ def add_server(args):
def set_note(alias: str, note: str):
"""Update server notes — safe for AI (no credentials exposed)."""
data, servers = load_servers()
if alias not in servers:
print(f"Unknown: {alias}. Available: {', '.join(servers.keys())}")
sys.exit(1)
alias = _resolve_alias(alias, servers)
for s in data["servers"]:
if s["alias"] == alias:
s["notes"] = note
@@ -331,9 +356,7 @@ def set_note(alias: str, note: str):
def remove_server(alias: str):
data, servers = load_servers()
if alias not in servers:
print(f"ERROR: Unknown '{alias}'")
sys.exit(1)
alias = _resolve_alias(alias, servers)
data["servers"] = [s for s in data["servers"] if s["alias"] != alias]
save_servers(data)
remove_from_ssh_config(alias)
@@ -395,12 +418,10 @@ def main():
if cmd == "--remove" and len(sys.argv) >= 3:
remove_server(sys.argv[2]); sys.exit(0)
# Server commands
# Server commands — exact match first, then fuzzy search by keyword
alias = cmd
_, servers = load_servers()
if alias not in servers:
print(f"Unknown: {alias}. Available: {', '.join(servers.keys())}")
sys.exit(1)
alias = _resolve_alias(alias, servers)
server = servers[alias]
if len(sys.argv) < 3: