「不是 AI 不好用,是让 AI 用好你需要先把自己累死。」「工具对 AI 友好,但对人不友好。这本身就是一个设计 Bug。」
屏幕还亮着,OpenCode中光标在终端最后一行的「What would you like to do?」那儿一闪一闪的。键盘上 Ctrl 键已经被我按得有点发油了。我心里在说:「怎么在AI CLI里选择系统文件中的文件/文件夹路径这么难?!」。

⭐点击『极客精益』→『...』→ 设为星标,看完觉得有用就点个赞👍分享🔄推荐❤️
终端的光标还在闪。
我盯着 OpenCode 的输入框,想让它帮我 review 一段刚写的代码。问题是,我得先告诉它那个文件在哪。
packages/billing-service/src/handlers/invoice.processor.ts
打错了。再来。
packages/billing-service/src/handler/invoice-processor.ts
还是错。
第三遍才打对。emmm... 手指悬在键盘上,这几个字母越看越陌生。不太想 review 了,只想睡觉。
这种感觉你肯定不陌生。AI CLI 工具写代码、改代码、查代码是真的强,但「选文件」这个操作,体验还停留在 1985 年。

01. 问题出在哪?
不是说这些工具没有努力。Aider 有 repo map,自动分析你整个仓库的符号结构,用 page rank 算哪些文件最相关。从技术角度讲,这东西挺酷的。Claude Code 有 @ 引用,在输入框里打 @ 就能模糊搜文件。OpenCode 更进一步,@ 触发 fuzzy search 外,还支持 @file#L37-42 这种行级引用语法。
怎么说呢。核心矛盾一直没解决:这些工具的设计哲学是「AI-first」,优先让 AI 读得方便,而不是让人输入方便。
Aider 的 repo map 压缩到 1K tokens 给模型当背景知识,模型很开心,但你要手动加文件还是得敲路径。Claude Code 的 @ 搜索确实方便,但如果你的项目有 10000 个文件,模糊搜索出来的前几个结果大概率不是你要的那个。OpenCode?一样的问题。
更隐蔽的代价是「大海捞针」。就算文件进了上下文,大量无关信息也会稀释模型的注意力。Token 花了,幻觉率反而更高。
坦率的讲,Cursor 里 cmd+p 搜文件,搜到了直接回车就打开了。VS Code 里点一下侧边栏就行。但在 CLI 里,你得打字。
我有时候甚至觉得,这些项目是不是默认所有人都能把项目结构背下来。

02. 各家的解法,走到哪了?
我调研了一圈,把主流 AI CLI 工具的「文件选择」方案列出来看看。不是做评测,是让你知道「大家都有这个问题,只是痛在不同地方」。
Aider 的做法最有野心。它在底层跑 tree-sitter 解析整个仓库,把所有类、方法、接口的符号抽取出来,然后用个性化 PageRank 排序,按相关性给模型呈现一份「地图」。你改 A 文件,它自动把 B、C、D 中相关的符号带进来。但问题是,repo map 只能给模型看,不能帮你选文件。你想手动加文件?还是敲 /add path/to/file.ts。
Aider 也提供了 --subtree-only 参数和 .aiderignore 文件来缩小扫描范围,在 monorepo 里只聚焦一个子包,或者排除 node_modules、dist 等目录。但这些属于「防守型配置」,可以减少噪声,但解决不了「人在终端里舒服地选文件」这个根本问题。
Claude Code 选择了另一条路。不建索引,让模型自己去搜。它给模型一堆底层工具(Search、Grep、Read),模型自己决定要读什么文件。好处是灵活,坏处是,如果项目没有好的 CLAUDE.md,模型就像没头苍蝇一样乱翻。我见过它为了找一个函数定义,翻了 8 个文件、搜了 5 次 grep、读完发现找错了。
Claude Code 有 .claudeignore 做软过滤、permissions.deny 做硬隔离,还支持 --add-dir 扩展可读目录。但这些说到底都是「限制 AI 能看什么」,不是「帮人类选文件」。
OpenCode 的 @ 文件引用是三者中最自然的,fuzzy search 做得也不错。但你得先知道你要找什么。输入 @order,它可能给你列出 30 个名字里带 order 的文件。哪个是对的?不知道。而且能选的文件也仅限于当前打开opencode的系统文件夹内的文件。
Copilot CLI 主要靠 /compact 命令手动压缩上下文,但文件选择基本依赖 IDE 的 @workspace 机制。纯 CLI 场景下选文件的能力,不比 Tab 补全强多少。
所以呢,至今没有一个 AI CLI 工具能让用户像在 IDE 里一样,用鼠标点一下就把电脑系统中任意文件选出来。
社区倒是用 fzf + Repomix 拼了一条路:模糊搜索选文件,再打包成上下文喂给 AI。这在 monorepo 里已经很能打了。但说到底还是终端思维,你仍然得先知道你要找什么,还得记住命令组合。万一你连文件名都记不住呢?
03. 解药:系统原生文件选择器 + Ctrl+O
讲到这里,你可能以为我要开始抱怨了。不是的。是时候给点实在的了。
问题其实很简单。终端缺一个「弹出文件选择窗口」的能力。
VS Code 有,Finder 有,甚至 Photoshop 都有。但终端没有。
好在,这玩意我们自己能做一个。今天先讲大概的技术实现方案思路。我正在开始做这个AI CLI中的文件选择工具,晚些时候可以开源出来给大家用。最终实现可能不一定完全按照下文的方案,因为实际开发中可能会遇到一些问题并优化。
macOS:三行 osascript
macOS 系统自带的 osascript 可以直接调用 AppleScript 的系统级文件选择对话框:
osascript -e 'set theFiles to choose file with prompt "选文件加到AI上下文:" with multiple selections allowed' \ -e 'set out to ""' \ -e 'repeat with aFile in theFiles' \ -e ' set out to out & POSIX path of aFile & "\n"' \ -e 'end repeat' \ -e 'out' 2>/dev/null跑一下试试。你会看到 macOS 原生的文件选择器弹出来,多选文件后回车,路径就打印到终端了。
选文件夹?换 choose folder 就行。
Linux:一行 zenity
Linux 桌面用户更简单,直接用 zenity:
zenity --file-selection --multiple --separator=$'\n' --title="Select files for AI context" 2>/dev/null把这一切绑到 Ctrl+O
这就够了吗?不够。每次要选文件先跑一段脚本,粘贴路径,那和手敲差不多痛。
真正的解法是,把这个操作焊死在你终端的快捷键上。
在 ~/.zshrc 里加上这一段:
# 自定义系统文件选择器,绑定 Ctrl+Ochoose-file-and-insert() { local selected_files="" if [[ "$(uname)" == "Darwin" ]]; then selected_files=$(osascript -e 'set theFiles to choose file with prompt "选择文件添加到上下文:" with multiple selections allowed' \ -e 'set out to ""' \ -e 'repeat with aFile in theFiles' \ -e ' set out to out & POSIX path of aFile & " "' \ -e 'end repeat' \ -e 'out' 2>/dev/null) elif [[ "$(uname)" == "Linux" ]]; then selected_files=$(zenity --file-selection --multiple --separator=" " --title="Select files for Terminal" 2>/dev/null) fi if [[ -n "$selected_files" ]]; then LBUFFER+="${selected_files} " zle reset-prompt fi}zle -N choose-file-and-insertbindkey '^O' choose-file-and-insert然后 source ~/.zshrc,打开任何一个 AI CLI 工具,比如 OpenCode、Claude Code、甚至普通的终端。
然后让AI帮你搞定:在输入框里按 Ctrl+O,运行choose-file-and-insert, 系统文件选择器弹出来。选文件,回车,路径自动回填到当前输入框里。
就这么简单。20 行代码,打通了 CLI 和 GUI 世界之间的墙。
说实话第一次按下去,弹窗出来那一瞬间,我愣了一下。终端的冷光里突然冒出一个 Finder 对话框,那种违和感,又爽又怪。

04. 高级玩法:Claude Code 的 fileSuggestion 拦截器
如果你用的是 Claude Code,还有一个更优雅的玩法。直接替换它内置的 @ 文件搜索行为。
Anthropic 在 Claude Code 里留了一个扩展点:fileSuggestion 设置。你可以把它指向一个自定义脚本,而不是默认的模糊搜索。
在 ~/.claude/settings.json 中配置:
{ "fileSuggestion": { "type": "command", "command": "sh ~/.claude/file-suggestion-gui.sh" }}然后写 ~/.claude/file-suggestion-gui.sh:
#!/usr/bin/env bashQUERY=$(jq -r '.query // ""' 2>/dev/null)PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"cd "$PROJECT_DIR" || exit 1# 输入 :gui 触发系统选择器if [[ "$QUERY" == ":gui" ]]; then if [[ "$(uname)" == "Darwin" ]]; then SELECTED_PATHS=$(osascript -e 'set theFiles to choose file with prompt "Select files to add to Claude context:" with multiple selections allowed' \ -e 'set out to ""' \ -e 'repeat with aFile in theFiles' \ -e ' set out to out & POSIX path of aFile & "\n"' \ -e 'end repeat' \ -e 'out' 2>/dev/null) else SELECTED_PATHS=$(zenity --file-selection --multiple --separator=$'\n' --title="Select files for Claude Code" 2>/dev/null) fi if [[ -n "$SELECTED_PATHS" ]]; then echo "$SELECTED_PATHS" | while read -r line; do if [[ -n "$line" ]]; then realpath --relative-to="$PROJECT_DIR" "$line" 2>/dev/null || echo "$line" fi done exit 0 fifi# 不是 :gui 就走默认的 fzf 搜索rg --files --hidden --follow --glob '!.git/' --glob '!node_modules/' 2>/dev/null | fzf --filter "$QUERY" | head -15效果是:在 Claude Code 输入框里打 @:gui,直接弹出系统文件选择器,选完自动转成相对路径回填到对话中。
这个技巧的价值在于,它没有破坏 Claude Code 的工作流,只是在「不合适键盘操作时」提供了另一个选项。
05. 往前走一步:从「选文件」到「选上下文」
弹窗选文件解决了最基础的交互问题。但如果你面对的是更复杂的场景,比如改一个支付 service,你得让 AI 同时看到它依赖的 DTO、repository 和测试文件。光靠鼠标一个个点还是慢。
高阶团队的做法是搭一套「上下文工程管道」:
1. 入口:git diff 定位变更文件,或者 dependency-cruiser 按依赖关系自动扩张 2. 打包:把选定文件交给 Repomix,用 --compress做 tree-sitter 压缩,只保留类签名和方法名,砍掉函数体,体积能降 80%3. 消费:压缩后的上下文喂给 Claude Code、Aider 或 OpenCode
这套思路的核心和 Ctrl+O 方案一样,把「给 AI 喂什么」的决定权交还给人,只不过从「选一个文件」升级到了「选一个经过压缩的结构化上下文包」。
说实话,这个方向我自己也还在摸索。但至少方向是清楚的。
如果感兴趣,可以从 Repomix 的 --compress 和 --stdin 开始试,配合 git diff 效果最好。
06. 不止是「省几步操作」
你可能觉得,就为了省几步操作,搞这么大阵仗?
怎么说呢。不是的。
当「选个文件」需要 10 秒心理准备的时候,你的工作流就已经被打断了。
打断不是指那 10 秒。打断是指你从「思考代码」切换到了「找文件」。这完全是两种认知模式。好的工具应该让这种切换成本趋近于零,就像你在 VS Code 里点一下侧边栏一样自然。
AI CLI 工具有个很有意思的现象:它们对 AI 太友好了,对人不太上心。Aider 给模型构建 repo map、Claude Code 让模型自己搜文件、Copilot CLI 自动压缩上下文,都是为了让 AI 工作得更顺畅。但让人选个文件这件事,没人认真解决。
这不合理。因为说到底,AI 是为你服务的。如果交互本身让你难受,那再强的 AI 能力也是白搭。
这句话开头就说过,工具对 AI 友好,但对人不友好,这本身就是一个设计 Bug。而且这个 Bug 不需要等官方发补丁。
我猜未来一年,我们会看到更多 AI CLI 工具开始重视「人机交互界面」这件事。不是加个简单的 TUI 就叫交互了。真正的交互是让你感觉不到工具的存在。理想态大概是「选择层→边界层→打包层」三层各司其职:你只负责点文件,工具自动判断哪些该进上下文、哪些该排除,再压缩成模型友好格式。全程不需要你敲路径或记命令。
在那之前,这几行 zsh 配置,大概是我能找到的最好的临时解药。

试试看。装好之后,找个深夜,打开你的 AI CLI 工具,按一下 Ctrl+O。
那种弹窗选文件、路径自动回填的感觉。
怎么说呢。有点像在 2026 年,提前用上了本该有的东西。
要不要也试试?我自己也没用太久,说不定还有坑。但你试完回来可以告诉我,我觉得值。
读者互动:你平时用 AI CLI 工具吗?最让你抓狂的操作是什么?评论区聊聊,我看看有没有比「选文件」更痛的。🤔

局限性说明:macOS 的 osascript 方案全版本均自带(无需额外安装),Linux 的 zenity 依赖桌面环境(GNOME/KDE),Windows 用户需要 PowerShell 替代方案本文未完整覆盖。Claude Code 的 fileSuggestion 拦截器需要 Anthropic Claude Code 版本支持(确认 2026 Q1 以上版本支持)。文中观点包含大量个人偏见。我确实更喜欢终端工作流,但承认不是所有人都需要这个方案。
延伸阅读:
• Building a better repository map with tree-sitter (Aider)[1] — Aider 的 repo map 技术详解,tree-sitter 怎么抽符号,PageRank 怎么排优先级,写得挺清楚 • Claude Code permissions & fileSuggestion docs[2] — 官方文档,fileSuggestion 拦截器的配置说明就在这里 • OpenCode TUI 文档 — @ 文件引用[3] — OpenCode 的 @ 引用用法,fuzzy search 的交互逻辑 • Repomix: Pack your entire codebase for AI[4] — 你要是想把整个仓库打成上下文喂给 AI,这个工具是 Node.js 生态里最好的 • fzf: 终端模糊搜索神器[5] — 配合 fd 和 bat,在终端里搜文件比 IDE 还快,社区上下文工作流的核心组件 • Codebase-Memory: Tree-Sitter Knowledge Graphs for LLMs (arXiv)[6] — 学术界的最新方案,用知识图谱替代 RAG,Token 消耗降到 1/120
相关标签:#AI编程 #CLI工具 #开发者体验 #OpenCode #ClaudeCode #Aider #Zsh #终端效率
引用链接
[1] Building a better repository map with tree-sitter (Aider): https://aider.chat/2023/10/22/repomap.html[2] Claude Code permissions & fileSuggestion docs: https://code.claude.com/docs/en/permissions.md[3] OpenCode TUI 文档 — @ 文件引用: https://docs.together.ai/docs/how-to-use-opencode[4] Repomix: Pack your entire codebase for AI: https://repomix.com/guide/usage[5] fzf: 终端模糊搜索神器: https://github.com/junegunn/fzf[6] Codebase-Memory: Tree-Sitter Knowledge Graphs for LLMs (arXiv): https://arxiv.org/pdf/2603.27277
夜雨聆风