用 React + Ink 在终端里「优雅搜索」:开源 CLI 设计与非交互模式实践
适合:喜欢命令行、想写 Node CLI、或对「终端 UI + 脚本化输出」感兴趣的前端 / Node 开发者。
仓库:bot-cli · npm 包名:
search-bot-cli· 全局命令:bot-cli
一、为什么要做这样一个工具
日常开发里,「打开浏览器 → 搜索 → 点开几条结果」路径很短,但在以下场景里,纯终端反而更顺手:
- SSH 到远端、或本机就想少切一次窗口;
- 希望结果列表结构化展示,而不是浏览器里一堆广告与折叠;
- 想把「搜索」接进自己的脚本、CI,甚至 AI Agent 的 tool 调用里。
于是有了 search-bot-cli (命令行里叫 bot-cli):底层用 DuckDuckGo HTML 结果 拉取标题、链接与摘要,无需申请搜索 API Key;上层用 React + Ink 做交互式终端界面,并支持 JSON / 纯文本非交互输出,兼顾「人看」和「机器读」。
二、功能一览
| 能力 | 说明 |
|---|---|
| 免费搜索 | 基于 DuckDuckGo,不强制配置密钥即可搜索 |
| 交互 TUI | 序号打开链接、剪贴板 + 默认浏览器联动 |
| 保存结果 | /save、/save json 导出到 output/ 目录 |
| AI 摘要 | /summary(可选,需 OpenAI 兼容 API 与 OPENAI_API_KEY) |
| 非交互模式 | --output json / --output plain,适合管道与自动化 |
技术栈概览:TypeScript + Node 18+、Commander 14、Ink 7、React 19、Cheerio、clipboardy、open。
三、安装:先认准包名与命令名
npm 上的包名是 search-bot-cli ,装好后在终端里执行的是 bot-cli 。若误装 npm install -g bot-cli,会装到 npm 上另一个同名包,与本文仓库无关。
bash
npm install -g search-bot-cli
(公司私服、registry 不同步等环境差异,可到仓库 README 查看说明。)
四、使用方式速查
交互搜索(默认进入 Ink 界面):
bash
bot-cli search "TypeScript"
非交互:只打印 JSON 后退出(脚本 / Agent 友好):
bash
bot-cli search "Rust 异步" --output json
# 或简写
bot-cli search "关键词" -o plain
查看子命令与参数说明:
bash
bot-cli --help
bot-cli search --help
交互模式下常用指令:1--10 打开对应结果,/save、/save json、/summary、/clear,Ctrl+C 退出。默认 TUI 依赖真实终端;json / plain 模式不渲染 Ink,可无 TTY、可管道重定向。
五、实现思路(精简版)
5.1 CLI 入口:Commander 子命令 + 输出分流
search <keyword> 作为子命令;通过 -o, --output 在三种模式间切换:
interactive:拉取结果后render(<SearchApp />);json/plain:格式化写入 stdout 后直接结束进程,不进入 Ink。
这样同一套搜索逻辑既能服务「人类点选」,也能服务「机器解析」。
5.2 搜索层:HTTPS + Cheerio 解析 HTML
对 DuckDuckGo 的 HTML 端点发起请求,用 Cheerio 选择器抽取 title、link、snippet,再截取前 N 条(当前为 10 条)。实现上注意 User-Agent、编码与页面结构变更带来的兼容风险------这是所有「爬 HTML」类工具的共同维护点。
5.3 交互层:Ink 里当 React 写
主界面在 SearchApp 中处理键盘输入、高亮、阶段状态(保存中、摘要生成中等),列表展示拆到 SearchResults;剪贴板、打开链接、写文件、调用兼容 OpenAI 的摘要接口则放在 utils/ 下,保持组件相对干净。
六、CLI 与 MCP:区别是什么,CLI 又好在哪里
Agent 要「动手」时,常见两条路:让模型生成并执行终端命令(CLI) ,或 通过 MCP(Model Context Protocol)把外部能力注册成结构化工具 。二者不是非此即彼,但取舍差异很大;下面把概念对齐,并说明在什么情况下 CLI 往往更划算 (也与本文 bot-cli --output json 的设计一致)。
6.1 各自是什么(一句话)
- CLI(命令行接口) :本机或 CI 上的可执行程序,用参数、环境变量、stdin/stdout 交互;输出可以是人类可读文本,也可以是 JSON 等机器可读格式 ,便于管道(
|)和脚本拼接。 - MCP :以 JSON-RPC 为主的协议,在 Host(如 IDE) 与 MCP Server 之间约定资源(Resources)、工具(Tools)、提示(Prompts)等;模型通过协议 发现 工具名与参数 schema,由 Host 代为调用。
MCP 由 Anthropic 在 2024 年提出后,已被多家 IDE / Agent 平台接入,用于把「数据库、文档、内部 API」等以统一形态挂到 AI 侧------这是它的主战场:标准化连接与权限边界。
6.2 核心差异(怎么接、成本从哪来)
| 维度 | CLI | MCP |
|---|---|---|
| 调用形态 | 一次(或多次)进程:命令 + 参数,stdout 即结果 |
常驻或按需连接:工具列表、schema、调用走协议消息 |
| 与 shell / CI | 天然契合:&&、管道、重定向、Cron / Pipeline |
通常由 Host 管理连接,较少手写 shell 编排 |
| 上下文占用 | 多数场景下模型只需 当前这一条命令(和必要说明) | 连接或枚举工具时,易把 大量 tool 定义 带入上下文(「schema 膨胀」是社区常讨论的点) |
| 调试体验 | 把同一条命令复制到终端重跑,报错栈与退出码一目了然 | 排障依赖 Host 日志与协议层,对人更像「黑盒」一步 |
社区与厂商文章里常提到:在 Token / 成本 和 可组合性 上,CLI 往往更轻;在 多系统集成、OAuth、企业审计与统一治理 上,MCP 更易做成产品线能力(例如 CircleCI 对 MCP 与 CLI 的对比、Firecrawl 的 Agent 场景选型)。具体倍数因任务与实现而异,不必迷信单一数字,但 「协议 + 全量 schema」带来的上下文压力 是结构性差异。
6.3 CLI 相对 MCP 更「占优」的典型场景
结合日常开发与本文工具特性,CLI 更值得优先的情况包括:
- 脚本化与管道 :搜索、格式化、再喂给下一步(
bot-cli ... \| jq ...),与 Unix 哲学一致;MCP 更偏「IDE 内一站式」,而不是 shell 组合。 - 调试与可重现:同一命令可脱离 Agent 单独执行,便于定位是模型指令错了还是环境/网络问题(这也是许多团队仍保留「CLI 作为真相来源」的原因)。
- 非交互、机器可读输出 :例如
--output json,Agent 只需约定少量 flag,而不必在上下文里长期挂载一整套 MCP tool 描述。 - 内循环(inner loop):个人本机、小团队、原型阶段------上线快、依赖少,不必先搭 MCP Server 与 Host 配置。
- 模型对「命令」的先验强 :训练数据里终端与常见 CLI(
git、curl、kubectl等)密度高,短命令 + 明确 stdout 契约 往往比塞一长段 schema 更省对话轮次与上下文。
业内也有「CLI is the new API」的说法:产品暴露稳定 CLI,比强迫每个集成方都实现 MCP Server 更容易被自动化与 Agent 消费------与本仓库「搜索能力 CLI 化 + JSON 模式」是同一思路。
6.4 MCP 仍然更合适时(避免误读成「否定 MCP」)
在需要 统一鉴权(如 OAuth) 、多租户审计 、跨应用实时拉取结构化资源 、或 IDE 深度集成工具发现 时,MCP 的工程化收益明显。生态上也在缓解 token 压力:例如 按需加载工具 、网关聚合、以及「把部分能力仍以子进程 CLI 落地」的混合架构------CLI 与 MCP 叠用 在 Cursor、Claude Code 等产品线里已很常见。
6.5 和本文项目的关系
search-bot-cli 选择 CLI + 可选 JSON 输出 ,本质是:用最小协议面(argv + stdout)服务人与 Agent,避免为了一次搜索在上下文里常驻大段工具定义;若未来要做「在 IDE 里一键搜 + 带鉴权的私有索引」,再考虑 MCP 或混合方案会更自然。
七、小结
- 终端 + React 并不是噱头:Ink 把状态、输入循环和布局表达得很接近前端日常经验,适合快速做出可用的 TUI。
--output json把同一能力开放给自动化与 AI 工具链,和「把能力做成 CLI 给 Agent 调用」的方向一致。- CLI 与 MCP:简单可组合、低上下文契约的任务,CLI 往往更直接;复杂多源治理与 IDE 级集成,MCP 更对口------可按场景组合,而非二选一。
- 安装时认准:包名
search-bot-cli,命令bot-cli。
若你对实现细节、DuckDuckGo 解析健壮性等话题感兴趣,欢迎在仓库提 Issue / 交流。