fix(cli): keep typing responsive by not blocking the keystroke loop

The interactive CLI input box runs its completer with
`complete_while_typing=True`, so `SlashCommandCompleter.get_completions`
is invoked on *every* keystroke. That completer does blocking I/O:
fuzzy `@`-file indexing shells out to `rg`/`fd` (up to a 2s timeout) and
file-path completion calls `os.listdir` + `stat`. Because the completer
was passed inline (never wrapped in `ThreadedCompleter`), all of this ran
synchronously on the prompt_toolkit event loop, stalling the render after
each key — very noticeable on WSL2 and other slow-filesystem setups
("typing in the prompt box being very latent").

Two fixes:

- Wrap the input completer in `ThreadedCompleter` so completion work runs
  off the UI event loop and never blocks rendering between keystrokes.
- Stop treating URLs as file paths in `_extract_path_word`: a token like
  `https://example.com/x` contains `/`, so it triggered `os.listdir` on
  every keystroke while typing/pasting a link (listing a bogus `https:`
  dir) for a completion that can never be useful. Skip any token with a
  `://` scheme separator.
This commit is contained in:
xxxigm 2026-06-17 09:21:24 +07:00
parent 5e01a5dbf1
commit b5be2ba276
2 changed files with 14 additions and 1 deletions

9
cli.py
View file

@ -12024,6 +12024,7 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
# Create the input area with multiline (Alt+Enter), autocomplete, and paste handling
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import ThreadedCompleter
_completer = SlashCommandCompleter(
@ -12039,7 +12040,13 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
wrap_lines=True,
read_only=Condition(lambda: bool(cli_ref._command_running)),
history=FileHistory(str(self._history_file)),
completer=_completer,
# complete_while_typing fires the completer on every keystroke. The
# completer does blocking work — fuzzy @-file indexing shells out to
# rg/fd (up to a 2s timeout) and path completion hits os.listdir/stat
# — so running it inline would stall the render loop on each key (very
# noticeable on WSL2/slow filesystems). ThreadedCompleter moves it off
# the UI event loop, keeping typing responsive.
completer=ThreadedCompleter(_completer),
complete_while_typing=True,
auto_suggest=SlashCommandAutoSuggest(
history_suggest=AutoSuggestFromHistory(),

View file

@ -1291,6 +1291,12 @@ class SlashCommandCompleter(Completer):
word = text[i + 1:]
if not word:
return None
# URLs contain "/" but are not local paths. Treating them as paths fires
# os.listdir on every keystroke while typing/pasting a link (e.g. an
# https:// URL becomes a listdir of "https:") — pure latency, never a
# useful completion. Skip any token with a scheme separator.
if "://" in word:
return None
# Only trigger path completion for path-like tokens
if word.startswith(("./", "../", "~/", "/")) or "/" in word:
return word