NEE's Blog

现在开发 TUI 变得简单了

February 14, 2026

本文翻译自 Building a TUI is easy now,原载于 Hacker News。

从怀疑到信服

2025年5月,当我第一次启动 Claude Code 时,我心想:哈,这挺可爱的。一个基于终端的编程代理……他们肯定是在迎合开发者。

但在前30分钟内,我被说服了:这将会非常巨大。以至于我们 Hatchet 团队讨论了是否应该围绕基于终端的编程代理构建一个新的产品线。

最终我们决定不这么做,但与 Claude Code 的第一次体验一直萦绕在我心头。事后看来,基于终端的代理会在开发者中拥有极快的采用曲线,这是如此简单而明显。

也许你也有类似的感觉——你正在开发的工具应该有一个 TUI(Terminal User Interface,终端用户界面)。也许你一直想自己构建一个 TUI。又或者你只是喜欢说 “too-ee” 这个词。

我想说:放手去做吧。这比我预期的要简单得多,只需要借助 Claude Code 的几个巧妙技巧。这可是来自一个怀疑者的真心话。我们之前曾尝试对我们的前端进行激进的、代理优先的重构,结果是一周内搭建完成,花了五周时间修复 bug,最后只能放弃。

相比之下,这个 TUI 主要由 Claude Code 驱动,但只用了几天时间就构建并发布了。你可以在这里查看实时演示:

实时演示:https://tui.hatchet.run

为什么要构建 TUI?

我一直想为 Hatchet 做一个 TUI。类似于 k9s,但是针对任务和工作流运行。我不确定其他人是否会觉得有用,我们甚至没有向社区宣布它,但在几天内我们就收到了用户非常积极的、主动的反馈。例如:

“伙计们,Hatchet CLI(尤其是 TUI!)做得太好了。它感觉比 UI 快得多了。”

我喜欢这条评论,因为它触及了我热爱 TUI 的核心——即使它使用与 UI 完全相同的 API,它们给人的感觉就是更容易使用。它们也与过去几年 Web 应用程序的发展趋势相反:TUI 是文本优先的、信息密集的,最重要的是,它们内联在你的代码中,防止不断的标签页切换。

由于我们的用户主要在 IDE 中开发 Hatchet 任务和持久工作流,我们希望提供一种体验,让工作流可以在终端中可视化和运行,而不是不断地在代码和浏览器之间切换。

技术栈

让我们深入了解细节。每个前端应用程序都从技术栈开始:如今典型的技术栈可能是 React、react-query、Tailwind、ShadCN,以及一些额外的 Tanstack 库。

TUI 开发也有等效的库——而且它们都由同一家公司维护!我指的是 Charm 技术栈:如果你不熟悉,Charm 团队一直在构建一套 TUI 库,使用起来非常令人愉悦。我主要使用了 Bubble Tea、Lip Gloss 和 Huh。不要被这些库可爱的性质所迷惑——它们的文档非常完善,并且有大量的示例。

虽然我发现构建 Bubble Tea 和 Bubbles 之外的任何自定义内容稍微困难一些,但这肯定比构建基于 React 的渲染引擎(如 Claude Code 内部使用的那个)要容易得多。

我最喜欢的一点是可以使用 Lip Gloss 和 Huh 主题轻松地将样式应用到任何 TUI 元素上,感觉很协调。然后我在整个 Hatchet CLI 中重用了这种样式,而不仅仅是在 TUI 中。例如,Hatchet CLI 中的大多数命令默认都是交互式的,使用 Lip Gloss 主题的表单:

测试

最重要的测试工具是 Claude Code:事实证明,基于终端的编程代理非常擅长驱动其他基于终端的工具。这意味着开发过程看起来像这样:构建一个组件或视图,编译你的 TUI,然后让 Claude Code 驱动第一轮测试。

我在 Hacker News 上看到一条评论,说使用 Claude Code 驱动 tmux 会话,该会话使用 tmux capture-pane 来存储渲染的视图并测试以确保它们看起来正确。这对于驱动第一轮测试非常有效,同时也确保了不断增多的视图能够继续正确渲染(即使是 Hatchet TUI,它是一个相对简单的 4 个主要视图集,但也包含至少 6 个其他替换视口的模态框)。以下是 Claude Code 在驱动 tmux 会话时”看到”的内容:

从历史上看,我对涉及 e2e 或前端测试的编码代理功能最大的挫折是反馈循环。但在这个案例中不是:LLM 是为在基于 ASCII 的环境中迭代而构建的。在 Claude Code 驱动第一轮测试后,我手动测试了每个视图,并为任何关键内容编写单元测试。经过几次迭代后,TUI 最终处于一个令人惊讶的稳定状态。迭代感觉是收敛的而不是发散的。

玩简单模式

当使用参考实现时,Claude Code 更加有效。我们的参考实现是我们现有的前端。所以我对 Claude Code 的大多数指令都引用了一组现有且非常具体的前端视图、组件或钩子。我们的前端没有做任何太疯狂的事情,我们尽量保持组件和视图尽可能简单,业务逻辑和 API 调用卸载到各种 React 钩子中。这给了 Claude Code 一个非常清晰的边界分离,可以先构建每个视图的业务逻辑,然后再构建视图。

我们还从使用 OpenAPI 规范生成服务器接口和 REST API 客户端中受益匪浅,这给了 Claude Code 一个简单的参考和自动生成的客户端来与我们的 API 交互。

我最初怀疑最难构建的组件是基于 DAG(有向无环图)的渲染器。作为背景,Hatchet 在编排器领域是独特的,它支持单任务、通过持久任务实现的持久执行,以及基于 DAG 的执行。渲染 DAG 是一个棘手的问题,在我们的前端中由出色的 React Flow 抽象出来,它处理了围绕正确渲染 DAG 和图形的大量复杂性:

Hatchet UI 中的 DAG 视图

将 React Flow 内部转换为 TUI 似乎不可行,所以我尝试了一种不同的方法。我经历了几次失败的迭代,让 Claude Code 构建它,然后我决定深入研究它。我在网上搜索现有的基于 ASCII 的图形渲染器实现,偶然发现了 mermaid-ascii。我克隆了这个仓库,让 Claude Code 指向它,写了几段提示,第一次尝试就让它渲染出了一个可工作的 DAG 渲染器。

它还不完全完美,但正在接近。

结果

总的来说,这花了大约 2 天的努力。值得注意的是,这是我第一次觉得使用 Claude Code 做一些非琐碎的事情比我自己做要快得多。前面提到的前端重构失败具有代理反模式的所有特征:极快地让一些非常有前途的东西工作,然后在复杂性和 bug 的浪潮中迷失方向,这些 bug 是由微妙的 bug 堆叠在微妙的 bug 之上造成的。一座纸牌屋。我们在一次艰难的事后复盘后恢复了前端更改。

这对我来说是一个巨大的转变:发布一个主要功能,在过去几周中经过我激进使用后一直可靠地工作,由编码代理驱动。虽然我们不会很快完全放纵我们的代理,但我们正在逐渐更多地使用它们,特别是对于不太关键的路径(如果你的 TUI 崩溃,希望它不会让你的生产环境崩溃。这对你的排队系统来说不是真的)。

也许这些教训对更有经验的工程师来说有点无聊和直观:构建一个具有紧密反馈循环、模块化设计、适当的规范、持续测试和部署的环境,等等。但我终于感觉自己进入了循环。

再次链接实时演示,很想听到你的反馈!

实时演示:https://tui.hatchet.run

关键要点

  1. TUI 正在复兴 - 对于开发者工具来说,TUI 提供了更快的体验和更少上下文切换的工作流程
  2. Charm 技术栈非常成熟 - Bubble Tea、Lip Gloss 和 Huh 提供了构建专业 TUI 所需的一切
  3. Claude Code 是 TUI 开发的利器 - 作为终端工具,它能更好地理解和测试 TUI 应用
  4. 参考实现很重要 - 使用现有的前端作为参考,让 AI 代理更容易理解需求
  5. 快速迭代是关键 - 紧密的反馈循环让开发过程收敛而不是发散

这篇文章展示了 AI 辅助开发的真正威力——不是替代开发者,而是加速开发和测试的反馈循环。对于终端应用来说,这种方式尤其有效。

comments powered by Disqus