Skip to content

feat(tui): resolve @ file mentions to real paths at submit time#1318

Open
UncertaintyDeterminesYou4ndMe wants to merge 3 commits into
MoonshotAI:mainfrom
UncertaintyDeterminesYou4ndMe:feat/at-mention-file-resolution
Open

feat(tui): resolve @ file mentions to real paths at submit time#1318
UncertaintyDeterminesYou4ndMe wants to merge 3 commits into
MoonshotAI:mainfrom
UncertaintyDeterminesYou4ndMe:feat/at-mention-file-resolution

Conversation

@UncertaintyDeterminesYou4ndMe

Copy link
Copy Markdown

Problem

The @path token is an input affordance: autocomplete inserts it, but the literal @ was sent through to the model verbatim. The model then went looking for a file whose name literally starts with @, failed, ran ls to discover the real name, and only then recovered — a wasted tool-call round trip on every file mention.

A second, related issue: for a scoped query like @~/Down, the fd-backed completion ran a recursive fuzzy search over the whole base with --hidden, so deep junk entries (e.g. ~/.Trash/**/Downloader.xpc, hidden browser profiles) outranked ~/Downloads/ itself.

Fix (two layers)

Submit time — mentions must never reach the model as literal @ (tui/utils/file-mention.ts):

  • Existence-gated resolution: a token that resolves to a real file/directory is rewritten to its verified absolute path with the @ stripped; anything else (@types/node, emails, plain prose) passes through untouched.
  • Handles relative / absolute / ~/ paths, quoted @"path with spaces", and CJK sentence punctuation glued to the token (@报告.docx,接着…) via longest-first retry candidates. ASCII . is never a split point, so a missing @src.md cannot mis-resolve onto an existing src/.
  • Wired into sendNormalUserInput and all three steerMessage branches. The transcript keeps the text as typed; only the outgoing parts carry resolved paths (same dual-track pattern as image placeholders).

Input time — a / in the query means the user is spelling a hierarchy (file-mention-provider.ts):

  • Scoped queries now list that single directory shell-style: basename prefix matches first, then substring (CJK names are often matched mid-name), directories before files, hidden entries only when the fragment itself starts with ..
  • Falls back to the existing fuzzy paths when the directory part doesn't resolve or nothing matches; bare fragments (@Down) keep the fd fuzzy behavior unchanged.
  • Mention tokenization (delimiters) is now shared between the completion provider and submit-time resolution, so a token the autocomplete inserts is guaranteed to tokenize identically on submit.

Tests

  • 12 cases for resolveFileMentions (CJK filenames, quoted spaces, out-of-workdir absolute paths, trailing CJK punctuation, existence gating, multi-mention, multi-line, unterminated quote, extension non-truncation).
  • 10 cases for directory navigation (single-level listing, prefix>substring ranking, ~/ expansion kept as typed, hidden-entry convention, junk exclusion, space quoting, fuzzy fallback, provider priority).
  • typecheck, oxlint --type-aware, and the existing editor/message-flow suites pass.

The @path token is an input affordance: autocomplete inserts it, but the
literal @ used to be sent through to the model, which then went looking
for a file whose name starts with @. Two-layer fix:

- Submit time: existence-gated resolution rewrites @mentions (relative,
  absolute, ~/, quoted, CJK trailing-punctuation) to verified absolute
  paths and strips the @; unresolved tokens (npm scopes, emails) pass
  through untouched. Applied in sendNormalUserInput and steerMessage;
  the transcript keeps the text as typed.

- Input time: a / in the mention query means the user is spelling a
  path hierarchy. List that single directory shell-style (prefix >
  substring, dirs first, hidden entries only for dot fragments) instead
  of recursive fuzzy search from the base, which surfaced deep junk
  like ~/.Trash/** above ~/Downloads. Bare fragments keep fuzzy search.
@changeset-bot

changeset-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 711873c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6a637078e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const extraction = extractMediaAttachments(text, this.imageStore);
// Resolve `@` file mentions before media extraction so both rewrites
// land in the parts we send; the transcript keeps the text as typed.
const mentionResolution = resolveFileMentions(text, this.state.appState.workDir);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve mentions for skill and plugin command args

This resolver only runs for normal prompts and steering; dynamic skill/plugin slash commands are dispatched through sendSkillActivation / activatePluginCommand with raw intent.args (apps/kimi-code/src/tui/commands/dispatch.ts:205 and :219). In a command such as /review @src/main.ts or a plugin command with an @ file argument, the editor still offers file-mention completion in slash-command arguments, but the activated skill/plugin receives the literal @src/main.ts, so the new submit-time guarantee is skipped for that workflow. Apply the same resolution to activation args before sending them to the SDK.

Useful? React with 👍 / 👎.

Skill and plugin slash commands dispatch through sendSkillActivation / activatePluginCommand with the raw argument string, bypassing the submit-time mention resolution applied to normal prompts. A command like /review @src/main.ts thus handed the skill a literal @src/main.ts. Resolve mentions at these two SDK boundaries as well.
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.

1 participant