3 天,我用 AI 给 Ghostty 做出了 Windows 版
正文Ghostty 是个终端工具Mitchell Hashimoto 写的就是做 Vagrant 和 Terraform 那位。 核心用 ZigMac 上套一层 SwiftLinux 上用 GTK。没有 Windows 版。我 fork 了一份三天后 Windows 版能跑了。第一个提交是 9 月 1 号凌晨两点半 9 月 4 号发了第一个版本。Windows 那部分 45 个文件4.8 万行 Rust代码我一行没写246 个提交每个 Co-Authored-By 都是 Claude。这篇主要讲讲这三天我在干什么。我用的工具就是我在做的东西得先说清楚这个 fork 是干嘛的不然后面的事讲不通。我 fork Ghostty 本来不是为了 Windows是为了做AI CLI编排就是基于Claude code、codex、opencode这类CLI工具来编排和做自动化开发。功能很简单选一个AI作为总管它能看到别的AI终端里多久没动了能读屏能往里打字能开新终端能把所有人拉进群聊配了一块任务面板用于协同。Windows 版就是拿这套东西做出来的就是用工具借助AI来迭代工具自身。开工一个总管三四个工人每个都是独立的 Claude Code 会话。总管建群把活拆成任务丢进面板一个个派下去。我定方向、拍板、盯着。有三条规矩是我定的。工人不许碰 git。 不许提交不许暂存git add 都不行全走总管。起因是有一次总管跟我说这两个人改的文件不冲突它是从其中一个工人的汇报里读来的但另一个却说有冲突。后来改成谁动了什么只能由动手的人自己说谁跟谁不冲突必须总管自己跑一次 git status 亲眼看。不许两个人同时编译。 Zig 的构建会先把某个中间产物删掉再重建这期间另一个人的构建必然死在一个跟他毫无关系的地方前两次我都以为是代码坏了。不许碰不是自己起的进程。 听着像废话但 AI 在清理环境的时候是真会顺手把你正在用的窗口关掉。为什么选RUST这层壳用什么写我没让 AI 定因为它决定后面所有的事。Linux 那份 Zig 是第一个被排除的。两万三千行听着能复用翻开全是 GTK窗口是 GTK 的窗口标签页是 GTK 的标签页输入法也走 GTK。换到 Windows 一行不剩。真正卡住我的是输入法。Windows 的输入法框架叫 TSF用 COM 写的。C 和 C# 调它很顺因为语言本身就认识 这套东西。Zig 不认识得手工弄那张函数表一个字段错位全崩。而输入法是 Windows 终端最难的一块也是中文用户开机十秒就会碰到的一块。Mac 的输入法接口 Swift 天生就有Linux 的是 D-Bus 加 C 接口Zig 接起来很顺。就 Windows 这个是 COM。Rust 有微软自己出的绑定COM 支持完整。选它输入法整个待在 Rust 这边Zig 一行 COM 都不用碰。Zig 那边最后只改了二十行左右——系统相关的信息压成了一个很小的结构原来两行 加第三行给 Windows 就完事。剩下的活全在 Rust而且 Mac 那层壳已经把同一份接口 实现过一遍等于有份参考答案。先啃最难的按常理该先把窗口开出来看见界面有成就感但我让AI先去啃输入法。这是整条路上唯一一件光看代码猜不出难度的事。要是比我想的难十倍我得在写两万行 后续代码之前知道。结果特别顺。输入法那部分四百多行头一回交叉编译出来的程序拷到 Windows 上一次 就通了敲 n i h a o候选词框浮出来按 1你好。日志里有一行我记到现在SetText 0..2 - 你把第 0 到第 2 格替换成你。这个字在内存里算一个字符在终端里占两格。 那个 2 要是算成 1候选框就偏而且只在中日韩文字上偏英文完全正常。 这种 bug 最难查。关键是那个 2 不是自己写死的是问终端核心要的跟核心排版用的是同一张宽度表。 它没图省事自己造一张看到这儿我才觉得这套干活方式靠谱。黑屏故障排查窗口能开了输入法通了。接上核心跑起来全黑。不是崩溃是安安静静地黑。该成功的全成功了窗口也在正常收到重绘制的通知就是一个像素都没有。后来摸出个规律先建个小窗口再放大就是黑的建的时候直接给最终尺寸画面就出来。埋日志量了一下画出来的区域 1000x670 ← 一直是建窗口那一刻的大小慢慢拉大六次这个数一次都没变过。但终端自己是清醒的。在全屏的终端里敲命令查行列数回答 143 列 41 行正好等于全屏宽高除以单个字符的大小一分不差。这个读数是命令行那头的程序报回来的。继续埋把每一环都打出来。链条是通的每一环的数都对。两个 agent 各自写了个测试方法两个都失败屏幕还是黑的。后来换了个办法让AI不看日志了截张图用画图工具量黑白分界线在哪。分界线落在横坐标 1000尺寸全对只是有旧尺寸那么大的一块矩形被画上了屏幕。 把日志改到最后一步真正往屏幕上贴图的地方目标1240x790 贴图区1240x790 画布1240x790 可绘区域(0,0,1000,670) ← 死在这儿前三个都跟上了最后那个卡在了旧尺寸。翻代码找到一个函数专管设这个区域搜谁在调它——全仓库零个调用者。这个可绘区域这辈子只被设过一次创建的那一瞬间按当时窗口大小设的之后再没人 管过。Linux 上为什么没这毛病翻 GTK 源码一眼就看见了它在每次窗口变大时替我们重设 了一遍。GTK 一直在帮忙擦屁股Windows 这边是块光板。更难受的是这事早就写在代码注释里那个函数旁边有句英文大意是在裸窗口上没有人会做这件事。读过那个函数的人没把它当缺陷读。回头看最开始那个建窗口时给全屏尺寸它之所以有效是因为它让那个只设一次的 区域恰好等于窗口大小。病一直都在只是被藏到了第一次之后的任何一次拉伸里。AI说全绿了不算数AI 写代码的难点不在写不出来在它会特别自信地跟你说工作完成了或者测试都通过了。举二个例子文件压根没编译新加了个文件接进项目编译报 0 错误 0 警告。看着挺好。其实把它加进项目的那行语句从来没执行。当时是一串命令连着跑中间断了一条后面的全没执行。而屏幕上那个退出码来自断点之前最后一条跑过的命令正好是 0导致AI说执行成功。后面是测试发现功能不对应才发现的代码缺失。错误的检查器脚本AI写了 16 道检查脚本。其中 4 道扫了 0 个文件然后打印一切正常退出码 0。扫遍整棵树没发现问题和一个文件都没扫到输出长得一模一样。发现的办法是测试检查器换件有一步把它们指向一个空目录看谁还敢说正常。现在每道检查都得带一句自检哪一行不出现就说明它本身没工作而不是它工作正常。一道永远不会失败的检查比没有检查更糟。还有一条是 Windows 独有的有些代码只在不是 Mac的时候才走。而在 Mac 上编译时编译器看一眼就知道这段 走不到干脆不分析了。所以平时那句在 Mac 上跑一遍确认没写坏对这部分代码完全不成立。解决方案是进行交叉编译 Windows 会红但在 Mac 上编译不一定会报错。61 道检查闸门上面这些坑有个共同点出问题的时候没有一条会大声报错。而 AI 最擅长产生一个看起来对的东西。所以我逼着它们把每次踩坑都变成一道自动检查。windows/tools/ 下现在有 61 个 Python 脚本提交之前全跑一遍。文件名本身就是 那条规矩a-position-is-not-an-identity.py 位置不能当身份 an-async-handle-is-not-copied.py 这种句柄不能按值复制 the-ui-thread-never-waits-forever.py 界面线程不许无限等待第一条是这么来的标签页和分屏格子的身份必须是永不重复的编号不能用它在数组里 的位置。格子会被拖来拖去、会被关掉用位置的话第 2 个会在某一刻悄悄变成另一个 格子不报错不留痕。还有个习惯比代码本身有用。状态文档里有两个词绝不混用已落地是代码写完了、编译得过、检查都绿了已验证是有一条在真机上跑出来的实际读数支持它。没有读数就不许写已验证哪怕这事明摆着是对的。还有一条文档里没标实测的默认就是没验过。方向是这么定的不是反过来。要是反过来用未验去标例外沉默就变成了一种承诺——读者看到会以为其余的都验过了。真机验证Windows 版没法在 Mac 上验旁边放了台 Windows 当测试机接了远程控制。 AI 自己上去装包、起程序、截图、读日志我基本不插手但这里的坑也不少。远程工具打字是绕过输入法的它直接合成字符送进去所以拿它测中文输入你会得出输入法没接通的结论换成真的发按键候选框立刻就出来。最麻烦的是发按键之前得先证明窗口在前台。Windows 某些情况下会静默拒绝把窗口提到前台函数不报错脚本一路跑完键落到别人窗口上。这时候键没发出去、键被别人吃了、这个功能坏了三种情况在日志上完全一样。后来写成硬规矩凡是要发按键的验证第一步写成点一下被测窗口不要写成把窗口置前。群聊这个功能本身是用群聊测的。在测试机上拿个本地跑的 27B 小模型当总管带三个工人在空目录里做个小项目。建文件、开终端、建群、派活、各自交件、汇总提交五步全通。最想看到的信号也出来了那模型被权限挡住之后自己去查自己是谁自己找到提权的办法自己重试成功。三天之后添加图片注释不超过 140 字可选9 月 4 号发的第一个版本之后又花了一周多打磨9 月 8 号打包给测试者 9 月 9 号冻结。真正在写功能的就是头三天后面都是真机验收和收尾。核心一共 72 个动作Windows 这边实现了 63 个明确拒绝 7 个还欠 2 个欠的两个是背景透明度切换和退出计时器。终端显示那一半根本不是移植的它就是同一份 Zig 核心Windows 这边只是给了它一个窗口我做的是外面那层壳。最后三天做出来不是因为哪个模型写得特别快。而是那三天里一直有四个AI在协同工作——一个啃输入法一个做标签和分屏一个查黑屏一个补菜单139 个提交是这么堆出来的。并行不难难的是一个人如果监管多个AI。哪个在等你授权、哪个二十分钟前就干完了在那闲着、哪个只是在等你确认设计。我原先的是每隔一会儿就得一个个看一遍都是碎片时间自己想做什么事情很容易被打断。所以我想法就是这活我得交给了另一个 AI 来做。总管盯着每个AI的终端屏幕哪个多久没动了、谁停下来了需要授权、谁干完了需要接着派下一个活儿而我只需要和总管一开始确认好设计和内容之后就不用再管了。群聊和任务面板也不是摆设——上下文清空、终端重开、第二天接着干面板上的活还都在。至于我自己这三天在干什么定方向、定规矩、和总管定设计、验收产出。跟 AI 干活工作量不在让它写代码而在建立一套让它没法骗你、也没法骗自己的验证方式。上面那 61 道闸、没标实测就当没验的默认值全是为这件事服务的。终端好用的部分全是 Ghostty 的功劳我只补了 Windows 这一侧。fork 不是重写上游更新会合进来。MIT跟上游一致。​https://github.com/Lugia123/polterWindows 这边还很新哪些验过哪些没验都在 dev-docs/windows/status.md 里逐条 标着。 碰到问题欢迎提 issue提到我这个仓库别提到上游 Ghostty 那边去。