Skip to content

fix(shell): search past file completion limit - #2551

Open
lihailong00 wants to merge 1 commit into
MoonshotAI:mainfrom
lihailong00:codex/issue-1610-path-completion
Open

fix(shell): search past file completion limit#2551
lihailong00 wants to merge 1 commit into
MoonshotAI:mainfrom
lihailong00:codex/issue-1610-path-completion

Conversation

@lihailong00

@lihailong00 lihailong00 commented Jul 23, 2026

Copy link
Copy Markdown

Summary

  • search beyond the first 1000 filesystem entries for selective non-Git @ completion queries
  • keep result and scan budgets bounded at 1000 and 10000
  • key query-specific fallback caches by fragment while preserving Git/scoped cache behavior

Reproduction and verification

A non-Git directory with 1100 early-sorting files deterministically hid a later matching target. The bounded query-aware scan now finds it, and a second regression test verifies consecutive different queries do not reuse stale subsets.

  • relevant completion/filter suite — 58 passed; 3 existing Windows-invalid filename cases excluded
  • targeted Ruff, Ty, and Pyright — passed
  • measured targeted lookup improved from about 0.119s to 0.053s locally

Closes #1610


Open in Devin Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review


self._cached_paths = paths
self._cache_scope = scope
self._cache_query = fragment if self._is_git is False and scope is None else None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Completion suggestions for one search can be reused for a different search in projects where git listing fails

The completion cache is tagged as having no specific search text (self._cache_query = ... else None at src/kimi_cli/ui/shell/prompt.py:745) even when the file results were actually narrowed to a single search text, so a later different search reuses the earlier search's suggestions.

Impact: In an affected project, typing one @ query and then a different one within a couple of seconds can show stale or empty completion results.

Mechanism: query-specific fallback results cached with a null query key

When _is_git is truthy but list_files_git returns None (git is present but git ls-files fails/times out), paths stays None and the code falls through to the query-aware walk with query=fragment (src/kimi_cli/ui/shell/prompt.py:735-741). The returned paths are therefore filtered to fragment. However, self._cache_query is written as fragment if self._is_git is False and scope is None else None (src/kimi_cli/ui/shell/prompt.py:745) — since _is_git is True, it is set to None. On the next call with a different fragment, the invalidation guard if cache_valid and self._is_git is False and scope is None: cache_valid = self._cache_query == fragment (src/kimi_cli/ui/shell/prompt.py:716-717) does not fire (because _is_git is True), and if the .git/index mtime is unchanged the cache is treated as valid, returning the previous query's filtered paths. The downstream FuzzyCompleter then re-filters that stale subset against the new fragment, typically yielding wrong or empty suggestions until the 2s refresh interval expires. The cache key should track whether the query walk was actually used (i.e. whenever the fallback walk ran with a non-None query), not whether _is_git is False.

Prompt for agents
In _get_deep_paths in src/kimi_cli/ui/shell/prompt.py, the fallback walk is invoked with query=fragment whenever scope is None and the git listing did not produce results (this includes the case where _is_git is True but list_files_git returned None). However both the cache-write (self._cache_query = fragment if self._is_git is False and scope is None else None) and the cache-validity guard (if cache_valid and self._is_git is False and scope is None: cache_valid = self._cache_query == fragment) only account for the _is_git is False case. As a result, when git is detected but ls-files fails, query-specific fallback results get cached under a None query key and are incorrectly reused for a different subsequent query within the refresh interval. Fix by keying the cache on whether the query walk actually ran (e.g. track a boolean of whether paths came from list_files_walk with a non-None query, and store/compare _cache_query accordingly), rather than conditioning on _is_git is False. Ensure the validity guard uses the same condition as the write side.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@ path completion hits 1000 file limit

1 participant