
我给手头的这个工具起名叫caveman中文直译过来就是“穴居人”。名字听着像开玩笑但它其实是我用过之后觉得最贴切的一个代号——这个工具做的事非常原始任务写在文本文件里操作靠命令行没有数据库没有网络依赖没有花哨界面。它解决的是我被各种“全家桶式”效率软件折磨到崩溃之后最朴素的那个需求随手记下来、随时查得到、数据永远归我。这篇文章就把这个叫 caveman 的小项目掰开揉碎讲清楚包括它名字的由来、核心设计思路、真实的开发过程、进入日常使用后的组合玩法以及极简工具在什么场景下该用、什么时候千万别用。如果你也受够了打开一个 App 要等三秒、数据存在别人服务器上、功能多到根本用不完的现状这篇内容应该能给你一点不一样的参考。1. 从“洞穴人”这个名字说起一个工具为什么要故意做旧1.1 软件复杂到让人喘不过气我做 caveman 之前试过不少任务管理工具。Notion 功能全但每次打开都要经历漫长的加载Jira 适合团队但为了看自己的待办还得穿过好几个面板手机上的效率 App 更别提了一个比一个精致一个比一个费电而且几乎所有在线工具都在“收集数据”这件事上有自己的想法。时间长了你会发现一个矛盾我明明只是想在指尖闪过一个念头的时候用五秒钟把它留住结果却要先登录、再建文档、再选模板、再调样式。等这一套走完那个念头早就没了。后来我想明白一件事对个人日常记录来说工具的复杂度和记录频率是成反比的。越是“强大”的工具启动成本越高你越是懒得用它。真正撑起日常记录的往往是那些不起眼的、随时能抓起来写的载体。caveman 就是在这种反思里冒出来的。1.2 三个设计约束决定了它的全部性格caveman 的定位不是一个功能齐全的软件而是一个故意“做旧”的个人任务台账。为了让这种“原始”不只是一句口号我从第一天就给它定下了三个硬性约束单文件可运行整个工具就是一个caveman.py日常使用只需python3 caveman.py list不需要安装、不需要服务、不需要前端构建。零第三方依赖只用 Python 标准库连requests都不用。这意味着不管机器是新的还是旧的Python 装了就能跑断网也能跑。纯文本即数据所有数据落在一个叫tasks.txt的文件里没有 SQLite 的二进制文件没有 JSON 的多层嵌套。这三个约束不是拍脑袋定的。单文件是为了降低使用门槛——你不需要记住“配置文件在哪”“数据库在哪”“日志在哪”一切都在一起零依赖是为了让生命周期足够长——Python 2 到 Python 3 的迁移很难但一个只用标准库的脚本十年后拉出来改两行还能跑纯文本则是为了数据主权——无论工具以后还活不活着.txt文件永远能用记事本打开。1.3 数据主权比“顺手”更重要的价值市面上大多数任务工具都在做“托管服务”你负责输入它负责替你保管和渲染。听起来很方便但你有没有想过如果这个产品停止运营了你的数据怎么办如果是订阅制你停止付费之后那些写了三年的日志还能不能导出成可读格式caveman 对这个问题给出了一个非常直接的答案你的数据本来就是一个文本文件有一份就在你自己电脑上。你可以拿它做 Git 版本管理可以用网盘同步可以随手拷进 U 盘可以放到任何一台有 Python 的机器上直接查看。它不产生“平台锁定”因为文本格式本身就是最底层的兼容协议。我身边很多开发者朋友第一次看这个工具都会问一句“就这”。但我用了几个月后最大的体会是工具的价值从来不在于它提供了多少个按钮而在于它在你最需要记录的时候能不能零阻力地出现。caveman 这个名字就是在提醒自己不要忘记这一点。2. caveman 的核心设计文本即数据库命令即思维2.1 为什么不用 SQLite也不用 JSON有人会说既然要写任务管理工具那至少用个 SQLite 吧或者把数据存成 JSON结构一目了然解析也方便。我最初也犹豫过但把几种方案放在一起对比之后发现纯文本反而是最适合这个场景的。存储方案人眼可读性手工修改友好度git diff 友好度依赖要求纯文本极好极好极好无JSON一般容易改错括号勉强可用标准库YAML较好缩进容易出错一般第三方SQLite差必须借助工具无法直接 diff第三方核心逻辑是这样的如果数据格式设计成“人眼也能直接读”那么即使某天 caveman 这个程序不见了你依然可以用任何文本编辑器打开tasks.txt一眼看明白有哪些任务、哪些做完了、优先级是什么。数据不应该被程序绑架程序只是数据的过客。这一点纯文本是做得最好的。JSON 也有它的优势比如解析标准、结构清晰但对于“手工维护”这个场景JSON 的括号和转义规则太反人类了。而且 JSON 文件在 git 里做 diff 的时候一行的改动往往导致整个结构看起来都变了实际几乎没法逐行审查。纯文本则完全不同每一行是一条独立记录哪个任务改了、哪条完成了git diff 出来非常清楚。2.2 tasks.txt 的任务结构和读写规则caveman 的数据文件结构长这样# caveman task file # format: [status] [priority] [date] [description] x [1] 2025-01-10 完成 API 接口联调 [2] 2025-01-12 修复登录页按钮错位 x [1] 2025-01-12 整理会议纪要并发送给相关同事 [3] 2025-01-13 给服务器做一次安全巡检每一行一条任务规则非常简单行首是x表示已完成空一个字符表示待办第三位是[优先级]1 最高3 最低接下来是YYYY-MM-DD日期日期后面剩下所有内容都是任务描述允许带空格、标点、方括号等任意字符。这里有个刻意设计已完成的任务行不会被删除只会把行首从空格改成x。这样一来tasks.txt就自动变成了一本流水账——你不仅能看到“现在还有哪些没做”还能回顾“过去某一天完成了什么”。这个特性在周报、月度总结的时候特别好用。关于任务编号我做了另一个故意简单的决定行号就是任务 ID。文件里第几行就是几号。虽然任务增删时行号会变但反正所有操作都是操作一个任务文件先list看到当前行号再对那个行号执行操作就行。这个方案省掉了自增 ID 的所有维护成本也避免了“删掉中间一条后编号断档”的问题。2.3 命令设计先想人类怎么说再想代码怎么写我见过不少工具功能不错但命令命名非常劝退比如task --create --title xxx --priority high。这种设计对程序是友好的对人不友好。caveman 的命令设计原则是先想人在真实对话里会怎么表达再映射到命令上。命令实际作用人在说话时的自然表达caveman list列出所有未完成任务“现在有啥事在排队”caveman add 任务描述 -p 1添加一条任务“记一下这个事优先级高”caveman done 3完成指定编号的任务“第3件事搞定了”caveman note 记录内容写一条工作日志“顺便记一笔”caveman review复盘今天的完成情况“今天这一天过得怎么样”所有命令都尽量控制在两三个单词内参数能省就省。比如添加任务时优先级默认是 2普通只有需要标红的时候才手动写-p 1done允许多个编号一次性传入比如caveman done 3 5 7对应人类的“连划三件事”这个动作。2.4 核心代码逻辑解析与写回解析逻辑我一开始用正则写后来踩了几个坑下一章详细讲改成位置式解析。核心思路很朴素顺着每一行从头往后读状态位、优先级、日期都是固定格式读完之后剩下的整段都当作描述不再做任何规则匹配。def parse_tasks(lines): tasks [] for idx, line in enumerate(lines, 1): if not line.strip() or line.startswith(#): continue done line.startswith(x) rest line[1:].lstrip() try: prio int(rest[1:2]) # 形如 [1] date rest[3:13] # 2025-01-12 desc rest[14:].strip() # 剩下的全当描述 tasks.append({ id: idx, done: done, priority: prio, date: date, desc: desc, }) except (ValueError, IndexError): # 非标准行直接跳过保证整个文件可读 continue return tasks写回的时候有一个很重要的细节必须做原子写入。也就是先把修改后的内容写到一个临时文件里再通过os.replace把临时文件替换成原文件。这样即使写的过程中断电了原文件也不会变成半个坏文件。def write_tasks(path, lines): tmp path .tmp with open(tmp, w, encodingutf-8, newline) as f: f.writelines(lines) os.replace(tmp, path)文本 파일이 특별한 건 아닙니다. 핵심은 “누구나 읽을 수 있고, 어떤 에디터로도 고칠 수 있고, 프로그램이 사라져도 데이터가 남는다”는 것입니다.3. 从零到上线caveman 的实际开发时间线3.1 第一版原型一个下午跑起来caveman 的第一版是在一个周日下午写出来的。当时手上有一个项目排期表乱成一团下午刚开完需求会我坐在电脑前把需求写在纸上能加任务能列出任务能勾掉任务数据要存在本地文本里这四个需求听起来少得可怜但几乎覆盖了日常任务管理的全部核心动作。我从命令行入口开始写用的就是 Python 自带的argparse命令设计成子命令的形式先写list再写add最后补上done。第一版大概三百行跑通之后我没有立刻加功能而是直接用真实的日常工作开始测试——这也是我觉得最重要的一步先放回真实场景里去用验证它是否真的解决问题再决定要不要继续加代码。大概一周之后我才补上了note和review。note对应的是“随手记一笔”的需求review对应的是“每天下班前看一眼今天完成了什么”。这两个需求在原型阶段就存在但我刻意压着不加因为想确认少了它们流程是不是真的跑不顺。结果发现只靠list和done任务台账更像是“待办清单”缺少“记录发生过的内容”这个维度。后来把note加上之后整个工具才从任务管理变成了一个真正的工作日志。3.2 踩坑实录文本解析的五个边界问题纯文本方案看起来简单但真正写起来还是会遇到不少边界情况。我把过程中踩过的比较有价值的坑列出来给同样想做文本型工具的朋友提个醒。第一个坑是任务描述里的方括号会导致正则解析错乱。最早我用^[ x] \[(\d)\] (\d{4}-\d{2}-\d{2}) (.*)$这一套正则去切行表面正常可一旦任务描述里出现[2025-01-12]或[bug#123]之类的内容匹配就直接失败了。解决方式很简单放弃正则改成位置式解析。状态位、日期、优先级都是固定长度或固定格式的前缀读完之后剩下的部分不做任何匹配全部视为描述。第二个坑是中文对齐问题。一开始我想让list输出得漂漂亮亮用str.ljust做列对齐结果中文字符的宽度和英文字符不一样输出直接错位。我纠结了几分钟后决定不做对齐。输出只用一个简单的[1]前缀加优先级标记反而更清晰。这个选择也符合 caveman 的整体气质——不追求美观追求一眼能看懂。第三个坑是Windows 编码兼容。我的主要使用环境是 macOS但部分脚本会在 Windows 上跑。最初用 UTF-8 写文件Windows 记事本打开就乱码。后来读写统一使用utf-8-sig编码带 BOM 的 UTF-8记事本能正常识别Linux/macOS 也能正常读两边都照顾到了。第四个坑是换行符差异。Windows 用\r\nLinux/macOS 用\n。如果直接按文本模式读写在 Windows 上可能会遇到多换一行的问题。我的做法是读文件时用newlineNone让 Python 自动识别写文件时统一指定newline保证跨平台行为一致。第五个坑是done 操作的幂等性。用户对一条已经完成的记录再次执行done程序应该怎么办我最初直接报错后来发现这个设计很烦人——因为在真实操作中你可能会连续对同一个编号按回车或者脚本批量执行时重复调用。最后改成如果任务已经是完成状态不做任何修改直接打印“已是完成状态”。命令要幂等这是工具脚本一个很重要的原则。3.3 平台适配编码、换行与终端别名caveman 的跨平台适配没有做什么高级处理真正的重点都集中在文件读写这一层因为其他逻辑都是纯 Python 字符串处理不涉及平台差异。我把文件读写单独封装成一个模块里面统一处理编码、换行、临时文件替换这样不管是 Windows 还是 macOS行为都是一致的。日常使用的时候给命令加个别名能省掉大量键盘输入。macOS 或 Linux 用户可以在~/.zshrc或~/.bashrc里加alias cmpython3 ~/tools/caveman.py alias cmaddpython3 ~/tools/caveman.py addWindows 用户可以创建一个cm.bat放在任意目录并加入 PATHecho off python %USERPROFILE%\tools\caveman.py %*加完别名之后日常操作基本就变成了cm看任务列表cmadd 给博客加一篇草稿记录任务cm done 5划掉一条。整个过程都是在终端里完成的速度和手感不是图形化应用能比的。3.4 给零依赖项目写自测脚本有人会担心一个零依赖的命令行工具怎么保证改来改去不出问题我用的是 Python 标准库自带的unittest测试用例直接操作临时文件目录跑完自动清理不需要任何额外安装。测试覆盖了几个关键场景添加任务后文件里是否追加了正确格式的行完成一个任务后行首是否从空格变成了x对已完成任务重复执行done文件内容是否不变任务描述包含中文、方括号、特殊字符时解析是否正常空文件初始化时能否正常运行不报错。这些用例虽然简单但给了我折腾后续版本的安全感。一个工具脚本不需要测试覆盖每一行但几个核心场景必须有回归保护尤其是格式解析这类容易改坏的地方。4. caveman 进入日常真实工作流中的组合用法4.1 每天的任务台账早晨列、傍晚清我用 caveman 的方式已经基本固定成了一日流程。早上到工位第一件事是cm屏幕上会按优先级列出当前所有未完成任务。这时候我会把今天必须推进的事排在心里然后开启工作。遇到新任务、新反馈随手cmadd一条优先级按“重要且紧急”的原则给 1普通顺手的事情给 2 或 3。这里有个经验不要事事都给 P1P1 给多了等于没有 P1。普通任务默认 P2只有真正影响今天目标的事情才标 P1。傍晚下班前我会跑一次cm review它会统计今天的完成数和剩余数同时把今天note的内容一起列出来。这套流程坚持下来之后我对“今天有没有做正事”这个问题有了非常直观的答案——不用回忆不用翻聊天记录打开终端跑一条命令一天的工作轨迹都在那里。4.2 让 caveman 融入自动化别名与定时任务caveman 是纯命令行工具所以天然适合和其他桌面自动化流程嵌在一起。我常用的一个做法是配合系统的快捷键绑定在 macOS 的 Automator 或第三方快捷指令里调用cmadd绑定一个全局快捷键比如Ctrl Option A。这样不管我在写代码、看文档还是开会只要脑子里闪过一个需要跟进的事项按一下快捷键输入框弹出来敲完回车任务就已经落到tasks.txt里了。整个过程三秒以内没有解锁手机、没有打开 App、没有等待加载。另一个用法是配合系统的定时任务。比如我每天下午 5 点半会跑一个任务自动执行caveman review并把输出重定向到当天的工作日志文件里。这样即使我某天忘记做复盘数据也会被自动保存下来事后补看完全没问题。4.3 多端同步与文件备份纯文本的天然优势纯文本的另一个巨大优势是同步和备份极其简单。我自己的方案有两层第一层是Git 版本管理。tasks.txt所在的目录初始化成一个 Git 仓库每天或每周 commit 一次。因为每一行都是一条独立记录git diff 看得清清楚楚哪天加了哪条任务、哪天完成了哪条、哪天写了什么笔记。这种“任务变更历史”在没有任何额外代码的情况下就自动获得了。第二层是SyncThing 或网盘同步。我用 SyncThing 把整个目录同步到手机和工作电脑上。在手机上也可以用任意文本编辑器直接查看tasks.txt虽然体验不如原生 App 舒服但应急查一条任务完全够用。纯文本同步几乎不会出现“文件损坏”的问题最多只是两个端都有修改时产生版本冲突而文本冲突的合并成本很低。4.4 从 txt 到周报几条命令的导出方案caveman 没有内置生成周报的功能但纯文本配合 shell 管道导出方案可以非常灵活。比如我要统计这周完成了多少条任务只需要grep ^x tasks.txt | grep 2025-01- | wc -l要列出本周完成的内容变成 Markdown 列表grep ^x tasks.txt | grep 2025-01- | sed s/^/ - [完成] /再配合note的内容一个工作周报就基本成型了。你还可以把这个导出命令写进一个report.sh每周五下午跑一次输出直接贴到文档或邮件里。整个过程没有任何图形界面但恰恰是这种“低科技”的做法让周报这件事变成了一条可复用的命令而不是打开一个软件找半天导出按钮。5. 极简工具的边界什么时候该用 caveman什么时候别用5.1 谁适合用 caveman我观察到的用户画像caveman 不是给所有人准备的。到目前为止我发现用它用得顺手的主要是这几类人开发者或重度命令行用户不排斥终端甚至觉得终端比图形界面更高效数据敏感者不希望自己的任务列表、工作日志存在第三方服务器上极简主义实践者意识到记录的关键在于“快”功能复杂度反而妨碍记录频率有长期主义心态的人希望自己的记录十年后依然可以用纯文本打开而不是被困在一个停止维护的 App 里。如果你符合其中的一两类用这类工具大概率会很顺手。但反过来如果你不希望接触命令行或者你更喜欢用手机随手记录那这个工具就不一定合适。5.2 caveman 不会做的事协作、提醒与复杂排期极简工具的代价是它放弃了很多现代软件的默认能力这一点必须说清楚。caveman不适合团队协作。它没有权限系统没有通知机制没有评论互动。同一个文件理论上可以多人编辑但冲突处理和同步协作都需要额外的手段这不是它擅长的事。如果你需要的是一个团队项目管理系统Jira、Trello、飞书这类产品仍然是更好的选择。caveman也没有主动提醒能力。它不会在你忘记某个截止日期的时候弹通知因为它根本没有后台进程。所有提醒都必须靠外部机制实现比如系统定时任务发通知、或者你自己养成每天看两次list的习惯。对提醒依赖很强的人这个工具会让你失望。caveman更不适合做复杂排期。它每条任务只有一个简单优先级和一个日期不支持开始时间、截止时间、依赖关系、里程碑这些概念。它更像一个“第二大脑的速记本”而不是一个项目调度器。把甘特图之类的需求往这个工具上套属于用错工具。5.3 “原始”的反思与后续可能的扩展做 caveman 的过程中我无数次被问到同一个问题“都什么年代了还用文本文件管任务”一开始我会解释理由后来我发现自己也经常思考这个问题的反面是不是我把极简当成了目的本身我的结论是极简不是目的而是手段。caveman 让我愿意记录原因是它足够快、足够可靠、数据足够可控。如果哪天有新的需求出现我完全不排斥给这个工具做扩展。目前我已经在考虑几个未来的方向给tasks.txt增加一个索引文件支持全文搜索加一个report子命令直接把周报导出成 Markdown 文件通过系统通知机制实现简单的“每日摘要提醒”支持多文件数据源比如把工作和生活分成两个 txt 文件。这些改动都不会破坏现有格式因为数据的根基是纯文本程序怎么变都不会影响数据本身。这也是为什么我敢把这个工具“做旧”——因为文本这种“旧格式”恰恰是它最不容易过时的部分。我把这个项目陆续维护了快一年代码量从最初的三百行变成现在的一千出头但使用方式几乎没有变过。我最满意的不是它有多少功能而是无论过了多久打开终端敲几个字数据就在那里。最后再分享一个我一直在用的小技巧把cm add的调用封装成一个系统级快捷键在任意窗口里按下快捷键弹出的输入框直接写任务描述回车后任务就进了 txt。这个动作从有想法到落盘不超过三秒钟比解锁手机打开 App 再新建任务快得多。如果你也被各种“功能过剩”的工具搞得心烦不妨从这类原始但可靠的方案开始试试。工具是拿来用的不是拿来供的——这是我做 caveman 一年来最大的体会。