Summary
Add @-triggered file path autocomplete to the TUI input panel, allowing users to type @ followed by a filename fragment and select from a ranked list of matching files via glob search, with the selected path rendered in cyan/green.
Motivation
When referencing files in conversations (e.g., "look at src/tui/app.js"), users must manually type full or partial paths. This is error-prone and slow, especially for deeply nested paths. A lightweight autocomplete triggered by @ would reduce typos, speed up file references, and make the TUI feel more polished.
Proposed Solution
- Trigger detection: In
InputPanel, detect when the user types @ and enter autocomplete mode.
- Glob search: Use
fast-glob to search the project root for files matching the typed fragment. Limit results to top 5 matches.
- Dropdown rendering: Render a dropdown list below the input panel with keyboard navigation (up/down arrows to navigate, Enter to select, Esc to dismiss).
- Selection: On selection, replace
@<query> with the full file path. Render the selected filename portion in cyan/green using chalk ANSI codes.
- State management: Track autocomplete state (
inAutocomplete, query, matches, selectedIndex) in the App component and pass it to InputPanel.
Key files:
src/tui/inputPanel.js — add autocomplete mode and dropdown rendering
src/tui/app.js — manage autocomplete state, pass to InputPanel
package.json — add fast-glob dependency
UI behavior:
- Type
@ → dropdown appears below input line
- Type more characters → list filters in real-time
- Up/Down arrows → navigate list
- Enter → select, replace
@<query> with full path, render filename in cyan
- Esc → dismiss dropdown, keep
@<query> as-is
- No matches → show "No files match" message
Alternatives Considered
- Tab completion: Could work but tab is already used for other purposes in some terminals. @ is more discoverable and less likely to conflict.
- Full fuzzy search: Could use a library like
fuse.js for fuzzy matching, but glob-based prefix matching is simpler and sufficient for file paths.
- Inline completion (like VS Code): Would require more complex rendering since Ink doesn't support true overlays. A separate dropdown below the input is simpler and still effective.
OpenSpec Note
This project uses OpenSpec for feature development. If this request is approved, I will:
- Run
/opsx:propose to generate a full proposal with specs and tasks
- Iterate on the design before any code is written
- Follow the task-driven implementation workflow
Additional Context
Ink does not support true z-index overlays, so the dropdown must be rendered as a sibling component below the input panel. The selected text color change can be achieved by splitting the input value into segments and rendering the filename portion with a chalk color wrapper.
Audit Findings (for Issue #792)
- src/tui/inputPanel.js:16-25 — InputPanel is a thin wrapper around ink-text-input. To add autocomplete, we need to extend this component to handle autocomplete mode: detect @ trigger, render dropdown below input, manage keyboard navigation state.
- src/tui/app.js:949-957 — InputPanel is rendered inside a Box wrapper in the App component. Autocomplete state (inAutocomplete, query, matches, selectedIndex) needs to be lifted to App and passed as props to InputPanel.
- src/tui/statusBar.js:60-97 — StatusBar renders below the input. The autocomplete dropdown must be rendered between InputPanel and StatusBar, not below StatusBar, to avoid pushing status bar content.
- package.json:77 — Ink 7.1.1 is used. Ink 7 supports multiple renderers and the new component model. Autocomplete dropdown must be a sibling component rendered conditionally below InputPanel.
- ink-text-input (dep) — The TextInput component captures all keyboard input. Autocomplete mode needs to intercept arrow keys and Enter before TextInput processes them, or use TextInput's built-in onSubmit/onChange hooks to manage state.
- chalk (dep, v6.0.0) — Already available for colored output. Can be used to render selected filename in cyan/green within the input display.
- fast-glob — Not currently a dependency. Would need to be added for file matching. Alternatively, use Node.js fs.readdirSync with recursive walk for a lighter approach, though fast-glob is more robust for pattern matching.
Summary
Add @-triggered file path autocomplete to the TUI input panel, allowing users to type @ followed by a filename fragment and select from a ranked list of matching files via glob search, with the selected path rendered in cyan/green.
Motivation
When referencing files in conversations (e.g., "look at src/tui/app.js"), users must manually type full or partial paths. This is error-prone and slow, especially for deeply nested paths. A lightweight autocomplete triggered by @ would reduce typos, speed up file references, and make the TUI feel more polished.
Proposed Solution
InputPanel, detect when the user types@and enter autocomplete mode.fast-globto search the project root for files matching the typed fragment. Limit results to top 5 matches.@<query>with the full file path. Render the selected filename portion in cyan/green using chalk ANSI codes.inAutocomplete,query,matches,selectedIndex) in the App component and pass it to InputPanel.Key files:
src/tui/inputPanel.js— add autocomplete mode and dropdown renderingsrc/tui/app.js— manage autocomplete state, pass to InputPanelpackage.json— addfast-globdependencyUI behavior:
@→ dropdown appears below input line@<query>with full path, render filename in cyan@<query>as-isAlternatives Considered
fuse.jsfor fuzzy matching, but glob-based prefix matching is simpler and sufficient for file paths.OpenSpec Note
This project uses OpenSpec for feature development. If this request is approved, I will:
/opsx:proposeto generate a full proposal with specs and tasksAdditional Context
Ink does not support true z-index overlays, so the dropdown must be rendered as a sibling component below the input panel. The selected text color change can be achieved by splitting the input value into segments and rendering the filename portion with a chalk color wrapper.
Audit Findings (for Issue #792)