feat(tui): resolve @ file mentions to real paths at submit time#1318
Conversation
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 detectedLatest commit: 711873c The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
There was a problem hiding this comment.
💡 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); |
There was a problem hiding this comment.
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.
Problem
The
@pathtoken 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, ranlsto 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):@stripped; anything else (@types/node, emails, plain prose) passes through untouched.~/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.mdcannot mis-resolve onto an existingsrc/.sendNormalUserInputand all threesteerMessagebranches. 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):..@Down) keep the fd fuzzy behavior unchanged.Tests
resolveFileMentions(CJK filenames, quoted spaces, out-of-workdir absolute paths, trailing CJK punctuation, existence gating, multi-mention, multi-line, unterminated quote, extension non-truncation).~/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.