Skip to content

feat: add file @-mention autocomplete with project file scanning#147

Merged
tellaho merged 20 commits intomainfrom
feat/file-mention-autocomplete
Apr 10, 2026
Merged

feat: add file @-mention autocomplete with project file scanning#147
tellaho merged 20 commits intomainfrom
feat/file-mention-autocomplete

Conversation

@tellaho
Copy link
Copy Markdown
Collaborator

@tellaho tellaho commented Apr 10, 2026

Category: new-feature
User Impact: Users can now mention project files by typing @ in the chat input, with fuzzy path matching and automatic filtering based on .gitignore rules.

Problem: The @-mention popover only showed personas. When working in a project, there was no way to reference files from the chat input — users had to manually type or paste file paths, with no discovery or autocomplete.

Solution: Extend the mention system to scan project working directories for files and surface them alongside personas in the autocomplete popover. Uses fzf-style fuzzy subsequence matching so path-based queries like crates/sprout-acp/.rs work naturally. The backend uses the ignore crate (from ripgrep) to respect .gitignore at every level, keeping results clean without a hardcoded skip list.

File changes

src-tauri/Cargo.toml / src-tauri/Cargo.lock
Add the ignore crate dependency for gitignore-aware directory walking.

src-tauri/src/commands/system.rs
New list_files_for_mentions Tauri command. Scans project roots using ignore::WalkBuilder which respects .gitignore, global gitignore, and .git/info/exclude. Enforces depth limits, result caps, symlink safety (follow_links(false)), and canonicalization to reject paths escaping project roots.

src-tauri/src/lib.rs
Register the new list_files_for_mentions command.

src/shared/api/system.ts
Frontend API binding for the new Tauri command.

src/features/chat/hooks/useMentionHandlers.ts
New hook centralizing all mention logic: loads project files on mount, builds deduplicated FileMentionItem lists from session artifacts + project files, owns fuzzy filtering, keyboard navigation, and selection handlers. Clears stale results immediately on project switch. Uses useEffect (not requestAnimationFrame) for post-selection cursor placement.

src/features/chat/ui/MentionAutocomplete.tsx
Extended to render two sections (personas + files) with section headers. Accepts pre-filtered results from the hook (single filtering pass per keystroke). Adds scrollIntoView({ block: "nearest" }) so arrow-key navigation keeps the selected item visible. Exports fuzzyMatch — an fzf-style subsequence matcher used across the mention system.

src/features/chat/ui/ChatInput.tsx
Wraps the textarea in a Radix Popover/PopoverAnchor for mention autocomplete positioning with viewport collision detection. Wires up useMentionHandlers and passes pre-filtered results to MentionAutocomplete.

src/features/chat/ui/tests/MentionAutocomplete.test.tsx
New test file covering rendering, scroll-into-view behavior, aria-selected state, and fuzzyMatch unit tests including path subsequence matching.

src/features/chat/ui/tests/ChatInput.test.tsx
Add typed mock for listFilesForMentions and test for file mention selection inserting the resolved path.

src/shared/i18n/locales/en/chat.json / es/chat.json
Add mention.filesTitle i18n key for the files section header.

tests/e2e/fixtures/tauri-mock.ts
Add list_files_for_mentions, get_home_dir, and path_exists to the Tauri IPC mock so E2E tests render correctly.

biome.json
Exclude .worktrees and .claude/worktrees directories from biome checks.

Reproduction Steps

  1. Open the app and navigate to a project that has working directories configured
  2. Start a new chat within that project
  3. Type @ in the chat input — the popover should show a "Mention a Persona" section and a "Files" section with project files
  4. Type a partial path like src/ — results should fuzzy-filter to matching files
  5. Use arrow keys to navigate — the selected item should stay visible in the scrollable area
  6. Press Enter on a file — the resolved file path should be inserted into the message
  7. Press Enter on a persona — the active persona should switch (existing behavior)
  8. Switch to a different project — the previous project's files should disappear immediately, replaced by the new project's files once scanning completes
  9. Verify that .gitignored directories (node_modules, dist, target, etc.) do not appear in results

Demo

Screen.Recording.2026-04-10.at.8.05.06.AM.mov

🤖 Generated with Claude Code

@tellaho tellaho marked this pull request as ready for review April 10, 2026 08:35
@tellaho tellaho requested a review from wesbillman as a code owner April 10, 2026 08:35
Copy link
Copy Markdown

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

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: 5297f70dd8

ℹ️ 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".

tellaho and others added 20 commits April 10, 2026 08:04
Extend the mention system to show session files alongside personas:

- MentionAutocomplete now renders two sections: Personas and Files
- FileMentionItem type for file suggestions (resolvedPath, displayPath, filename, kind)
- MentionItem union type (persona | file) for unified selection handling
- useMentionDetection accepts files param, filters both lists on query
- confirmMention returns MentionItem instead of Persona
- Extract mention handlers into useMentionHandlers hook (keeps ChatInput under 500 lines)
- File data sourced from ArtifactPolicyContext.getAllSessionArtifacts()
- Selecting a file inserts its resolved path into the message text
- Selecting a persona still switches the active persona (unchanged behavior)
- Add i18n key mention.filesTitle for the files section header
Replace manual absolute positioning with shared/ui Popover primitives
so the dropdown gets viewport collision detection and portal rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a Tauri command that scans project roots for files usable in @ mention completion.\n\nThe scan deduplicates roots and file paths, skips common generated directories,\nlimits traversal depth and result count, and runs in a blocking task to avoid\nstalling the async runtime.\n\nExpose the command through the frontend system API so chat mention handlers can\nrequest project files.
Load project file paths from configured working directories and merge them\nwith session artifacts in mention autocomplete results.\n\nWhen selecting a project, mention suggestions now include matching files even\nbefore they have been attached in the current session, and retain deduped\nabsolute paths for insertion.\n\nAdd a chat input test for file mention selection and include the Spanish\ntranslation for the files section heading.
Add scrollIntoView({ block: "nearest" }) via ref map so the active
item stays visible when arrowing through the popover list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace includes() with an fzf-style subsequence matcher so queries
like "crates/sprout-acp/.rs" match files whose path contains those
characters in order (e.g. acp.rs, config.rs in that directory).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The loadedRootsKeyRef dedup guard prevented the second mount in React
StrictMode from fetching files, while the first mount's response was
cancelled by cleanup. Remove the ref guard — the sameStringArray check
in setProjectFilePaths already prevents unnecessary state updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…cement

Use a pendingCursorRef + useEffect triggered by text changes instead of
rAF to set focus and cursor position after mention selection. This is
React-idiomatic — the effect runs after React flushes the new text into
the DOM, avoiding timing hacks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Expand biome.json includes array to multi-line format
- Exclude .worktrees and .claude/worktrees from biome checks
- Add biome-ignore for intentional text dependency in cursor effect

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Type the listFilesForMentions mock with explicit parameter types
instead of rest spread to satisfy tsc --noEmit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The mock IPC fallback returns null for unknown commands, which crashes
the mention handler expecting a string array. Add the mock so E2E
draft tests can render the chat view.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the hardcoded should_skip_dir list with the `ignore` crate
(from ripgrep) which respects .gitignore, .git/info/exclude, and
global gitignore. This automatically handles project-specific ignores
instead of maintaining a static list of directory names.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fixes:

1. File scanner: add follow_links(false) explicitly and canonicalize
   all paths to reject any that escape the project roots. Prevents
   symlink loops and path leakage.

2. Mention filtering: useMentionDetection hook now owns all filtering
   and exposes filteredPersonas/filteredFiles. MentionAutocomplete
   renders pre-filtered results instead of re-running the same fuzzy
   match on every keystroke.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Collapse redundant persona header branches into a single condition
- Remove replace(/\s{2,}/g, " ") from mention insertion which silently
  ate intentional double spaces (markdown, code blocks)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents stale files from a previous project appearing in mention
results while the new scan is in flight.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tellaho tellaho force-pushed the feat/file-mention-autocomplete branch from d5a4a30 to 649943d Compare April 10, 2026 18:04
@tellaho tellaho changed the title feat: file @-mention autocomplete with project file scanning feat: add file @-mention autocomplete with project file scanning Apr 10, 2026
@tellaho tellaho merged commit 7705281 into main Apr 10, 2026
8 checks passed
@tellaho tellaho deleted the feat/file-mention-autocomplete branch April 10, 2026 18:45
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