
你有没有过这种瞬间打开一个号称提高生产力的任务管理软件先等三秒启动再花十分钟调整看板分组、设定提醒、选皮肤主题好不容易想记一条待办又发现新建任务的弹窗里全是些非必填但看着不填就难受的字段。我反正是受够了。于是某天晚上我花了四个小时给自己写了一个叫caveman的命令行小工具专门用来记待办和备忘。它没有界面没有数据库没有任何网络请求数据就是一个纯文本文件。两个月用下来它成了我使用频率最高、最没有心理负担的个人工具。这篇东西就是把我的设计思路、踩过的坑以及一些能直接抄走的用法整理出来。适合和我一样厌倦重工具、想构建轻量个人管理流的人参考。不管你是程序员、产品经理还是平时只想记点零碎事情的人读完你都能在半小时内复刻一个自己的版本。1. 立项动因为什么一个现代开发者要做回“穴居人”先说清楚我不是什么极简主义原教旨主义者。手机里该装的 App 一个没少在线文档、笔记软件也都在用。但在“个人待办”这件事上我发现一个很反直觉的现象工具越重我越不想打开它最后真正完成的往往不是软件里排了优先级的任务而是脑子里偶然记住的那几件。1.1 重工具的问题不是慢是“管理成本超过收益”拿我之前常用的 Notion 来说。功能是真的强数据库、关系表、看板、日历视图全都有。但每次往里面加一条任务我得先想清楚这条该放在哪个 database和哪个页面做关联要不要加标签状态用单选还是多选这些问题的本质是我在管理任务之前先被任务管理软件本身管理了。一条普通待办比如“买牛奶”在现实世界里只需要 2 秒钟记住但在重工具里可能需要 30 秒来建档。当单条记录的成本超过记录本身的价值时这个工具就已经在帮倒忙了。Todoist 这类 App 稍微好一点但它的标签体系、项目分层、优先级队列对个人日常使用来说依然是过度设计。我每天新增的任务不超过 10 条其中 80% 活不过当天。为了这 10 条数据我却要维护一套“项目-区块-标签-截止日期”的结构怎么算都不划算。1.2 “caveman”这个名字想表达什么起这个名字的时候我脑子里想的是穴居人没有文字系统没有记事本他们只是在石头上刻一道痕提醒自己明天要去哪里打猎、哪里取水。这个场景特别符合我对个人备忘的终极想象——记录的动作必须足够原始原始到不会产生任何心理负担。所以 caveman 的设计原则从一开始就是反着来的不追求功能的完整性只追求记录的“零摩擦”。不追求数据结构的优雅只追求“人眼直接能看懂”。不追求跨设备同步的顺畅只追求“文件复制粘贴就能备份”。说白了我希望这个工具像一块石头一样可靠。放在兜里不沉掏出来就能用丢了也不心疼——因为内容本身已经在我的电脑里了。1.3 我参考的遗产todo.txt 与 org-mode 的极简血脉caveman 不是凭空想出来的。它站在两个著名的“纯文本管理”传统肩膀上。第一个是 todo.txt。它的核心思想就是“一行一条待办”用前缀字母表示优先级比如(A) 写季度报告。这个思路给了我很大启发格式足够简单任何编辑器都能打开任何脚本都能处理。但 todo.txt 的优先级标记和项目标记project对我来说还是稍微繁琐了一点。我试过一段时间发现自己经常纠结“这条到底该给 A 还是 C”反而忘了记录本身。第二个是 org-mode。它是 Emacs 里的神器级插件把日程、笔记、项目文档全部融合在一个纯文本文件里。我那时候没勇气跳进 Emacs 的深坑但 org-mode “一个文件即一个数据库”的理念我特别认同。caveman 只用其中最基础的子集日期、标签、上下文其他一概不要。所以我做 caveman 的时候刻意避开了两个极端既不要 todo.txt 那样的强制优先级字段也不要 org-mode 那样的庞大语法体系。它只保留三样东西日期、标签、位置全部放在行尾解析规则简单到可以口算。2. 三命令模型把交互砍到只剩增、查、结caveman 的命令行交互极其直白整个工具就只有三条子命令分别对应个人任务管理的全部动作添加、查看、标记完成。没有编辑没有拖拽没有日期选择器。2.1 数据文件长什么样所有数据存在一个纯文本文件里默认路径是~/.caveman/notes.txt你也可以通过环境变量CAVEMAN_FILE把它指到任意位置。文件内容大致如下2025-03-01 给组里的新人搭开发环境 #工作 办公区 2025-03-01 买牛奶和鸡蛋 #生活 超市 2025-03-02 重写季度复盘模板 #工作 2025-03-02 给路由器换根网线 #生活 家庭 x 2025-03-01 给组里的新人搭开发环境 #工作 办公区每行一条。未完成的任务不带前缀已完成的任务在行首加一个x和空格。这样设计的好处是文件本身就是数据库你看文件就等于查库。任何文本编辑器都能打开哪怕 caveman 这个程序明天不在了你的数据也依然是可读的纯文本。2.2 三条子命令的设计动机add命令做且仅做一件事——往文件末尾追加一行。它不管你今天的任务排序不问你优先级甚至连重复任务的合并都不做。为什么这么“笨”因为追加是成本最低的写操作。你不需要考虑“这条该插在哪里”打开终端输入完就结束了永远不会有排序焦虑。list命令负责查询。默认只显示“未完成”的任务可选参数包括--all显示全部和--today只看今天。再加上可选的标签和上下文过滤参数比如caveman list #工作 caveman list 家庭 --todaydone命令按“行号”标记完成。比如caveman done 3就把第 3 行未完成任务的行首加上x。我一开始想做成“删除”但后来发现保留完成历史特别重要。月底复盘时你可以清楚地看到自己这个月到底干成了多少件事而不是清空之后一片空白。完成痕迹本身就是一种正反馈。2.3 为什么不做更“友好”的语法在早期版本里我设计过一套复杂的参数语法试图向传统任务管理软件看齐。比如caveman add --task 写周报 --priority high --due 2025-03-01。结果我很快发现一个规律每增加一个可选参数就多一层心智负担。你明明只打算记录一件三秒钟的事却要先想清楚要不要填--due、--priority这些参数在 80% 的场景下都是多余的。后来我彻底简化了设计。可选信息全部用位置约定而不是参数名表达——日期写在行首标签用#前缀上下文用前缀。这样既保持了表达力又不需要记忆任何参数选项。注意这种“克制”是有代价的。当你想要表达一条带截止日期的复杂任务时caveman 的文本格式就不那么够用了。但我的观点是个人待办里 90% 的任务都是当天能完成的为 10% 的复杂任务去建设一整套复杂系统不值得。3. 从行文本到结构化查询一个纯文本文件撑起整套逻辑有人可能会问一个纯文本文件怎么支撑“按标签过滤”“按日期查询”这些操作答案是多做一步“即时解析”。每次查询时对文件内容现读现拆不做索引反正文件很小解析时间在毫秒级。3.1 行格式协议与解析顺序为了确保解析不出歧义我制定了一套简单协议行首如果匹配YYYY-MM-DD就提取为日期不匹配则任务无日期。行尾出现的#单词视为标签单词视为上下文。剩余部分就是任务内容内容中可以包含空格。关键点在于解析顺序不要一开始就按空格把整行切碎否则含空格的内容会裂开。正确做法是先用正则把标签和上下文“摘”出来再处理剩下的内容。我用 Python 写了核心解析逻辑只依赖标准库import re def parse_line(line): line line.rstrip(\n) done line.startswith(x ) tags re.findall(r(?!\S)#\S, line) for t in tags: line line.replace(t, , 1) contexts re.findall(r(?!\S)\S, line) for c in contexts: line line.replace(c, , 1) m re.match(r\s*(\d{4}-\d{2}-\d{2})?\s*(.*), line) return { done: done, date: m.group(1), content: m.group(2).strip(), tags: tags, contexts: contexts, }这个函数就是整个工具的核心。后续所有查询、统计、导出功能都建立在它返回的字典之上。正则里的(?!\S)是个小技巧它要求#或的前面必须是行首或空白字符这样“C#真香”这种中间的#就不会被误识别成标签这个细节帮我避开了后面会提到的引号大坑。3.2 过滤链路一次查询是怎么发生的当执行caveman list #工作 --today时程序按这个顺序处理读入文件全部行逐行调用parse_line解析成结构体。先按完成状态过滤去掉所有doneTrue的行。如果有--today按今天的日期精确匹配。如果有标签或上下文参数用集合交集判断。输出时给每行加上行号方便done命令引用。这套逻辑虽然简单但运行效率完全够用。一个月积累两三百行记录解析总耗时基本在 10 毫秒以内。即使用到几千行也只是毫秒级的问题。纯文本方案的性能瓶颈远比你想象得晚。3.3 完成状态为什么要用x前缀而不是移走我最初的设计是把完成的任务搬到另一个done.txt文件里理由是让主文件保持干净。后来放弃了这个方案因为“单文件”的价值远大于“文件内整洁”的价值。所有历史记录在同一份文件里备份只需要复制一个文件统计全年数据只需要跑一次解析。x前缀加上之后list命令默认过滤掉它主视图依然是干净的但数据没有丢。这个选择和“保留完成痕迹”的理念是配套的。你看到的不是一条条被删掉的待办而是一条条被画上完成标记的记录。每次grep ^x 2025-03 notes.txt | wc -l数一下当月的完成数特别有成就感。4. 从能用到好用实测踩过的四个坑理想很丰满现实很骨感。caveman 从“能用”到“好用”中间踩了不少坑。这里我把真实遇到过的四个问题完整记录下来每个都附上排查过程和最终解法。如果你打算自己实现一遍这些经验可以直接帮你少走弯路。4.1 中文宽度与表格输出错位最先踩的坑是输出排版。早期版本想把任务列表输出成对齐的表格类似1 | 2025-03-01 | 给组里的新人搭开发环境 | #工作 | 办公区 2 | 2025-03-01 | 买牛奶和鸡蛋 | #生活 | 超市在 Python 里用format按固定字节宽度填充。问题在于中文字符在终端里占 2 个单元格宽但len()算出来只有 1。结果就是只要内容里混了中文表格立刻错位行与行的竖线对不齐。我试过引入wcwidth库来逐个字符计算显示宽度确实能解决对齐问题但让工具多了一个第三方依赖。后来我想通了一个记录工具需要的是可读性不是像素级对齐。最终的输出格式改成了1 [2025-03-01] 给组里的新人搭开发环境 #工作 办公区 2 [2025-03-01] 买牛奶和鸡蛋 #生活 超市每行自然长度不强行补位。终端天然支持长行换行虽然偶有折行但读起来并不难受。有时候放弃“完美排版”反而能获得更好的工程体验。4.2 引号、井号与“这到底是不是标签”第二个坑来自 shell 和文本格式的纠缠。当你在终端执行caveman add 他说“你好” #闲聊时shell 会先把双引号吃掉程序收到的是他说你好 #闲聊。这还算温和更难缠的是#符号。如果任务内容里本身就带 #比如程序员常写的“重构 #auth 模块”那么这一行会被解析出两个标签#auth和#模块明显不符合预期。我最初用re.findall(r#\S, line)粗暴提取结果错判频繁。后来改成(?!\S)#\S要求#前面不能是非空白字符。这个改动的效果是#auth因为在行中前面有空格会被识别成标签而 “C#真香” 里的#前面是 “C”不会被识别。逻辑上更合理了。至于引号问题我的处理原则是记录时不做任何反转义原样写入文件。因为查询时也不需要精确匹配引号过滤全靠标签和日期内容里的引号只是给人看的。少一层转义就少一类 bug。4.3 时区、时区还是时区第三个坑和“今天”这个概念有关。早期版本里--today过滤的逻辑是先扫描文件里所有日期取最大的那个日期作为“今天”。听起来没错但跨午夜时立刻出 bug假如你 23:50 记了一条任务文件里最大日期就是当天过了 12 点再看工具依然认为“今天”是昨天因为文件里没有比昨天更新的日期。这个 bug 的根因是把“查询时间”错误地依赖了“数据里的最新时间”。修复方式很简单查询时直接用系统时间datetime.now().date()作为“今天”和数据里有没有这个日期没关系。这给我一个很深的教训——工具的逻辑应该以现实时间为基准而不是以数据推断时间。4.4 编码与路径Windows 用户的特殊眼泪第四个坑比较小众但值得提一下。我用 Python 写这个工具open()文件时如果不显式指定encodingutf-8在 Windows 上会默认用 GBK 读取中文内容直接乱码。解决办法很简单所有读写操作都显式传入encodingutf-8。另一个是路径问题。在 Windows 上用os.path.join(~, .caveman, notes.txt)拼接路径容易踩反斜杠转义的坑。后来我改用pathlib库的Path.home() / .caveman / notes.txt跨平台省心很多。如果你在 Linux 或 macOS 上做可能永远遇不到这两个问题但一旦要分享给 Windows 用户它们会一个不落地出现。提示如果你也想写自己的版本强烈建议从第一行代码就使用pathlib和显式encoding别等踩完坑再补。5. 一个月的真实使用数据与适用边界工具好不好嘴说没用。我连续用了一个月每天坚持把所有要办的事、临时冒出来的想法全部记进 caveman。一个月后数据文件大小不到 30KB累计记录了 200 多条任务。启动时间、查询时间都是瞬间完成没有任何一次卡顿。5.1 和主流工具的硬核参数对比为了说服一个坚持用 Todoist 的朋友我做了一张对比表把 caveman 和几个常见工具放在一起比。目的不是证明谁更好而是帮你看清楚取舍。维度NotionTodoistcaveman启动时间1-3 秒1 秒左右10 毫秒以下离线可用受限需要联网完全离线数据所有权在云端在云端完全在本机数据格式私有数据库私有格式纯文本可读可迁移移动端有有暂无可通过文件同步变通定时提醒支持支持不支持团队协作支持支持不支持学习成本中等偏高中等10 分钟以内每月成本免费/付费免费/付费免费看完这张表你就明白caveman 走的是“数据主权 极速记录”的路线。它牺牲掉的是提醒、移动端、协作这些现代功能换来的是零打扰、零成本、零绑定。5.2 它适合谁不适合谁这一个月的实测让我对 caveman 的边界有了很清楚的认识。适合的人群命令行基本操作无障碍、对数据隐私比较在意、喜欢纯文本可迁移的人以及那些“任务少但杂”、经常脑子里闪过“要记得做某事”的人。caveman 的“秒开秒记”特性特别适合捕捉转瞬即逝的想法。不适合的人群需要手机端随时记录的人因为目前没有移动端需要任务提醒和到点推送的人需要多人共享任务列表的团队场景以及习惯看板、甘特图这类可视化视图的管理型用户。对这些人我反而建议继续用成熟商业工具不要勉强自己迁就命令行。5.3 我后来加的两个小扩展stats 与 week一个人单文件工具的好处是想扩展就扩展不怕破坏生态。我在第二个月给 caveman 加了两个小命令依然保持极简。caveman stats统计本周总共完成了多少条任务。实现就一句话——解析文件统计当前周内x前缀行数然后按天分组输出。它不需要任何新的数据结构只是把已有的解析结果做了个聚合。caveman week生成一份周回顾文本。把所有“本周完成”的任务按天汇总输出一段类似工作日志的 Markdown 文本用于周五下午写周报。我顺手写了个脚本把它生成的内容自动粘贴到公司周报模板里省了不少时间。这两个扩展的共同特点是新增功能不改变既有数据格式不引入外部依赖一个 Python 脚本文件始终保持在几百行以内。这种“克制地生长”正是 caveman 和那些越做越臃肿的商业软件的根本区别。使用这套工具快两个月我现在最大的感触是真正能坚持用下去的个人管理系统靠的不是功能有多全而是它待在角落里不碍事、需要时一伸手就能抓到。最后分享一个小技巧我把caveman add绑定到了终端快捷键上任何时刻想记录点什么一个组合键就能弹出输入框几秒钟完成记录。这种“随手刻一道痕”的感觉才是穴居人真正的生存法则。