
最近我把手机里最后一个待办事项软件给删了。倒不是说它不好用而是我发现自己每天花在“整理待办软件”上的时间比真正去做事的时间还多。一个偶然的下午我花两小时写了一个极其简陋的命令行工具名字就叫 caveman——穴居人。这个项目核心只有一个想法把任务写进纯文本文件用命令去增删查改不建数据库、不上云、不搞订阅文件在哪任务就在哪。它适合受够了各种效率工具的学习成本、想回归“用最笨办法记事”的人。这篇文章完整记录了我从设计到日常使用的全过程包括每一个命令、遇到的坑和解决方案你可以直接照着复现。1. 这个叫 caveman 的项目到底解决了什么问题1.1 为什么我抛弃了花哨的效率软件我算是个重度任务管理使用者。用过很多工具看板、日历、笔记、清单。但最后发现工具越多越乱。有几个痛点让我最终决定离开它们打开应用要等加载加个任务要点好几层菜单数据虽然在云端但离线完全没法用功能太多光学习标签、过滤器、权限就要花一个星期。这还没算上各种会员订阅和同步冲突简直像养了一个需要天天喂食的电子宠物。我说一个实际场景。有一次开会对方让我记一下“下周三前把方案初稿发给张工”。我掏出手机解锁、打开应用、切到对应项目、点新增、填标题、选日期、选优先级、选标签然后保存。这一套下来少说三十秒还经常因为忘了选日期被应用提醒“没有设置截止时间”。等我把这三十秒乘以每天十几次一个月就是两个多小时全都浪费在“录入”这个动作上而不是“做事”上。caveman 想解决的就是这类问题。它把任务管理拉回到最原始的“拿笔在石头上刻一笔”的状态打开终端输入cm add 发方案初稿给张工完事。整条路径没有数据库没有后台服务没有通知不需要网络权限。数据就是你本地的一个或几个 Markdown 文件按行存取每行一个任务。想看任务就cm list做完了就cm done 3。这个工具本身不会给你弹任何提醒因为它的哲学是“你打开它的时候它就是你的提醒”。这背后是一个很朴素的道理工具的复杂度不应该超过它管理的对象的复杂度。任务本身只是一行文字但很多任务管理软件却为它配上了几百 MB 的安装包和一套完整权限系统。caveman 的方向是反着走的——尊重任务的本质用最小的代价完成记录与查询。它不是去适配你的工作流而是让你回到一种更接近本能的工作方式想到什么刻下来做完什么划掉。1.2 “回到洞穴”的设计理念名字 caveman 不是随手起的。Cave man 的意象是“原始人”原始人记事不用数据库他们在洞穴墙壁上画符号。这个项目的设计哲学就是向原始人学习不做多余的事不存多余的数据不怕数据丢失因为文件就在眼前。具体落地成四个原则一切皆文件。任务、笔记、归档全部是纯文本任何编辑器都能打开。一切皆命令。所有操作都可以通过简短的 shell 命令完成方便自动化。一切皆本地。默认不联网不上传不锁数据你拥有全部所有权。优先级排序靠“位置”不靠“字段”。越早写进文件的任务越靠前这已经是一种天然的排序。为什么要强调“位置即优先级”因为很多软件把优先级做成一个字段反而让排序变得很假。你有十个“高优先级”的任务哪一个先做在 caveman 里你只需要把明天要做的任务放在文件顶部列表输出时自然越靠前越重要。这个规则零学习成本也避免了每次都要纠结“这个任务到底是 P0 还是 P1”。这套理念的一个直接后果是它特别适合三种人一是开发者本来就在终端里工作二是自由职业者需要的是轻量、快速的个人事务管理三是每天要处理大量琐碎事务但不想折腾软件的人。如果你想拿它做团队协作不好意思这个工具暂时不支持多人同时在线因为它压根就没有“在线”这回事。2. 核心设计拆解纯文本、命令、和一条铁律2.1 任务文件长什么样caveman 的存储目录默认在~/.caveman/也可以自定义。我建议在环境变量里指定比如export CAVE_HOME$HOME/Documents/cave目录结构非常简单$CAVE_HOME/ inbox.md # 收集箱所有新任务先到这里 today.md # 今天要做的事 archive/ # 归档目录按月份存 md 文件每个文件里面都是标准的 Markdown checkbox 列表。例如 inbox.md- [ ] 发方案初稿给张工 2025-06-20 #work - [ ] 预约牙医 2025-06-21 #personal - [ ] 整理本周代码评审意见字段约定很轻后面跟日期表示截止时间#后面跟标签表示所属领域或场景。至于负责人、预计工时、关联需求这些一概不要。真需要的时候直接在行尾加文本即可比如“负责人我”或者“预计1小时”。不需要结构化因为任务本身的自然语言已经足够表达这些信息。为什么用 Markdown 而不用 SQLite 或 JSON因为 Markdown 是给人读的也能被机器解析。你用文本编辑器打开能看懂用 grep 能查询用任何编程语言都能处理。相反SQLite 需要专门工具JSON 不适合手写。更重要的是Markdown 是人类和脚本之间的公共语言。我甚至能把这份任务文件直接放进公司文档系统不丢格式。一个文件一个“用途”这算是一条铁律。inbox.md 只做“收集”today.md 只做“当日执行”归档文件只做“历史记录”。很多任务工具很强大但它把“收集、规划、执行、回顾”四个环节混在同一个界面里人很难分清当下该干什么。caveman 用文件名把环节拆开你打开 today.md不需要思考就是从第一行开始做。2.2 命令怎么映射到文件操作整个 caveman 本质上就是一个 Python 脚本加几个 shell 包装函数。核心操作只有六七个命令cm add 任务内容追加到 inbox.mdcm list [tag]显示 inbox.md 和 today.md 中的所有未完成任务cm push 行号把某一行从 inbox.md 移到 today.mdcm done 行号把某一行的[ ]改成[x]cm open 行号把某一行的[x]改回[ ]cm edit 行号用默认编辑器打开对应文件cm archive把今天完成的条目移动到 archive/2025-06.md看到没有每个命令都对应一个非常直接的文件操作。cm add的核心逻辑就是一行echo - [ ] $* inbox.mdcm done 3的实现也不复杂用 sed 把第三行的[ ]替换成[x]sed -i 3s/\[ \]/[x]/ inbox.md # macOS 需要空字符串参数这里要特别说明Linux 的 sed -i 后面不需要空字符串macOS 需要。所以我最后用 Python 写了统一入口避免平台差异。Python 脚本的好处是跨平台、字符串处理更稳处理路径和中文也更好。安装只需要系统里有 Python 3没有其他依赖。很多人问为什么不写成一个压缩的二进制或者打包成 App因为没必要。一个一百多行的 Python 脚本直接能读、能改、能审计这不正是“原始”精神吗我不需要维护一个巨大的工程也不需要担心版本升级破坏我的数据格式。2.3 为什么是命令行而不是图形界面你可能会想都什么年代了还回到终端敲命令。但正是这个“退化”反而带来了几个实打实的好处。首先是速度和心智负担。我做过简单的计测在终端里输入cm add 买牛奶到回车平均耗时 2.3 秒打开手机待办应用从解锁到保存大约需要 8 到 12 秒。差距看起来不大但“保持思路不被打断”的价值远高于那五六秒。写代码时手本来就在键盘上接到一个任务顺手敲进去不会打断编程状态。其次是可组合性。命令行的每个动作都可以被脚本化。比如配合 cron每天 8 点自动把cm list的结果推送出来配合终端通知把逾期的任务显示在屏幕右上角。图形界面应用很难做到这种“任意拼装”因为它不暴露底层数据更不提供编程接口。最后是可审计。人一旦离开图形界面就会被迫去看“数据本身”。你会发现任务其实只有那么多很多待办软件用颜色、角标、弹窗制造出来的“任务爆炸”有一半是软件自己制造的噪音。每天打开终端敲cm list看到二十行以内的未完成任务心里特别平静。当然命令行不是没有门槛。如果你完全没接触过终端第一次会有点不适应。但好在命令一共就六个每个都很短半天就能上手。看完后面的实操部分你会觉得这比很多图形界面的新手指引更友好。3. 从零搭建你的 caveman 环境安装与初始化3.1 环境准备只需要 Python 3 和你的终端先说环境要求三个东西Python 3.7 以上macOS/Linux 自带Windows 建议装官方版、一个终端macOS 的 Terminal、Windows 的 PowerShell 或 Git Bash、Linux 的任意 shell、文本编辑器写代码的肯定都有。没有数据库没有 Node.js没有 Docker没有“运行环境初始化向导”。安装过程就是下载一个脚本、加一个可执行权限、配置一个别名。我把脚本放在~/.caveman/caveman.py然后在 shell 配置文件中定义一个函数caveman() { python3 ~/.caveman/caveman.py $ } alias cmcaveman这里用函数而不是直接 alias是为了以后可以灵活添加预处理逻辑。如果你用的是 zsh把这两行加到~/.zshrcbash 用户加到~/.bashrc。改完后执行source ~/.zshrc或重开终端cm命令就生效了。当然你也可以不使用别名直接运行python3 ~/.caveman/caveman.py但相信我一旦开始高频使用六个字符的cm和四十多个字符的全路径比起来体验差很多。设置别名这二十秒很值得。3.2 初始化脚本让“caveman”脱口而出第一次运行时脚本会自动检测CAVE_HOME目录是否存在不存在就创建并生成 inbox.md、today.md、archive/ 三个基础资源。我建议你手动跑一遍初始化cm init这条命令会输出caveman initialized at /Users/yourname/Documents/cave created: inbox.md created: today.md created: archive/为什么不让用户自己 mkdir因为初始化可以顺便写入一些模板比如在 today.md 顶部放一行“今天最重要的三件事”的注释在 inbox.md 放一行使用说明。这些注释在帮助记忆的同时也会成为后来阅读文件的线索。反正我实际使用时每天都会看一眼这些提示。还有一个小细节脚本会把CAVE_HOME的路径记住写入~/.caveman/config。以后你写别名或做同步可以直接读取这个配置不用到处硬编码。配置文件内容很简单CAVE_HOME/Users/yourname/Documents/cave EDITORcode3.3 配置项与路径管理caveman 的配置项不多但每一个都影响日常使用。最重要的三个第一是CAVE_HOME。默认在~/.caveman但我强烈建议改成你常用网盘或同步目录里的某个文件夹比如~/Documents/cave。这样就算换了电脑把文件夹复制过去就能无缝迁移。我自己的方案是把它放在坚果云、Dropbox 这类目录里配合已有的同步机制做备份没网的时候本地照样用有网的时候自动同步。这只是一种通用做法完全不涉及任何特殊网络通道。第二是EDITOR。cm edit命令会调用这个变量指定的编辑器比如codeVS Code、vim、nvim。如果没设置脚本会退回默认的vi。我推荐用code因为 Markdown 表格、代码块渲染更好看编辑任务时随时能看到预览。第三是默认的“任务文件前缀”。比如我想把每日任务文件名不是 today.md而是2025-06-20.md那就要把脚本里的日期逻辑改一下。但这属于高级定制一般用户先不用管。路径管理是大坑尤其 Windows 上。如果你用反斜杠C:\Users\名字在 Python 字符串里容易出问题。建议所有路径统一用正斜杠/Python 和 shell 都认。另外路径中不要带空格其实带了也能处理但因为脚本是 shell 调用$和 Python 的sys.argv会帮你处理好。真正需要警惕的是“路径里恰好有空格且没有用双引号包裹命令”的情况后面第 5 部分我会详细介绍。4. 实操过程我用 caveman 管理一周工作任务4.1 添加任务三秒完成接下来我演示一个完整的工作周但为了能说细节我压缩成“周一早晨”的场景。早上 9 点打开终端。今天的第一件事是把脑子里冒出来的事情全部倒出来cm add 给客户回邮件确认报价 cm add 完成登录模块的单元测试 cm add 预约明天下午的设备调试 cm add 写周报初稿四条命令敲完inbox.md 变成了- [ ] 给客户回邮件确认报价 - [ ] 完成登录模块的单元测试 - [ ] 预约明天下午的设备调试 - [ ] 写周报初稿注意我没有输入任何标签和日期。原因很简单收集阶段最重要的是“不挡路”。先把任务落袋后面再慢慢整理。你要是非得分也可以在 add 时带上#work或2025-06-21脚本原样写入不影响后续查询。这个阶段我踩过最大的坑想在一开始就把所有字段补全结果想半天“这个任务属于哪个项目”反而把收集变成了负担。所以现在的原则是先无脑收集等整理时再补充必要标签。如果任务必须今天做我会在 add 之后立刻cm push让它进入 today.md。4.2 查看任务按上下文和优先级筛选上午十点处理完一批邮件该认真看任务了。执行cm list输出INBOX 1 [ ] 给客户回邮件确认报价 2 [ ] 完成登录模块的单元测试 TODAY 3 [x] 晨会准备 4 [ ] 预约明天下午的设备调试如果你想只看某个标签比如工作相关的cm list #work脚本会 grep 所有未完成任务里包含#work的行。这是纯文本最爽的地方没有索引也能秒出结果因为任务量通常就几十行。如果你想把 inbox 里某条任务提为“今日任务”用cm push 2。执行后第 2 行会从 inbox 移除并追加到 today.md。为什么不是直接编辑因为移动任务不仅涉及删除和新增还要在 today.md 的顶部插入。脚本实现的思路是先读取源文件取出行内容删掉原行再在当前日期文件开头加一行。这样 inbox 是纯粹的收集today 是你今天真正要推进的事。这个设计让我特别受用的一点是当同事临时插进来一个“很急”的需求我不会立刻把它塞进今天而是先放进 inbox等真正评估完再 push 到 today。这个过程天然帮我过滤了很多“伪紧急”。4.3 完成任务与归档不删文件下午三点左右我把登录模块的单元测试跑完顺手修了两个边界用例。这时 inbox 里的事项已经不再代表“当前要处理的事”应该把今天真正完成的任务挑出来。回到终端输入cm list看到第 2 行是“完成登录模块的单元测试”直接cm done 2然后这条任务的状态就变成[x]但它还留在当前文件里。这么做有一个心理暗示完成任务后不要立刻从视野中消失你能看到今天确实做完了几件事。等到一天结束再统一归档cm archive归档逻辑很直接把当天所有[x]的任务逐行追加到archive/2025-06.md然后从 today.md 中移除。这样后来的文件不会越来越长。很多人问为什么不用任务状态字段statusdone因为对文本来说[ ]和[x]是 Markdown 的标准语法任何 Markdown 编辑器都能渲染不需要额外解释。使用标准语法等于让任务文件天然获得了一整套生态可以渲染、可以转 HTML、可以被 CI 解析。4.4 与日历和笔记打通不打破工作流caveman 不是万能的日历管理、长文笔记这种事它不擅长。我的做法是用“分工”而不是“融合”有确切时间的事件会议、航班、缴款截止进系统日历。需要持续输出的内容项目方案、会议纪要、学习笔记进单独的笔记工具。行动项、待办、可以两分钟内完成的小事全部进 caveman。怎么打通其实不需要 API只需要一个约定任何任务都可以写一句“详见某文件”。比如- [ ] 根据《项目方案V3》补充测试计划 #work然后《项目方案V3》就是另一个系统里的文件名。这样信息自然流转不需要任何插件。如果你一定要自动化也可以让日历导出 ICS再用脚本解析生成 caveman 任务。但这属于进阶玩法而且容易把简单系统搞复杂。我先不推荐除非你一天有超过二十个带时间的事件。5. 常见问题与排查技巧实录5.1 中文乱码和编码问题第一个遇到问题的场景基本都在 Windows或者 macOS 上改了特殊区域语言的用户。现象是中文任务写入后cm list显示乱码或者cm done匹配不到。原因基本就是 Python 默认读取文件时没有用 UTF-8。我后来在脚本里统一加了# -*- coding: utf-8 -*- import sys, io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)并且在读写文件时都指定encodingutf-8。如果你自己改脚本也要注意这一点。另外在 Windows 的 PowerShell 里执行cm建议先设一下$OutputEncoding [System.Text.Encoding]::UTF8否则脚本输出的中文也可能变。实操中最稳的验证方法用cm add 中文测试然后type inbox.md看内容。如果文件里正常那基本没问题。还有一个容易被忽略的点如果你在 macOS 上创建的文档是 UTF-8拿到 Windows 记事本打开可能显示乱码那是因为 Windows 记事本对无 BOM 的 UTF-8 识别不友好。我的经验是在 Windows 上用 VS Code 打开而不是用系统自带记事本。5.2 路径解析与空格问题“路径里有空格”是新手几乎必踩的坑。假设你的CAVE_HOME设置为/Users/me/My Documents/cave在 shell 里执行cm list如果脚本内部拼路径时没有加引号就会变成ls /Users/me/My Documents/caveshell 会把My和Documents/cave当成两个参数直接报错。解决办法有两个方向一是根本不要用带空格的路径。在初期直接选择~/cave或~/Documents/cave这种路径省心。二是如果你必须用带空格的路径在 Python 脚本里用os.path.join和列表参数调用不靠 shell 拼接。比如所有读取文件的函数都用path os.path.join(self.home, inbox.md)这样才能稳。我在脚本里就是这么做的。如果你想省事最简单的测试方式是在终端里执行echo $CAVE_HOME如果输出路径带空格你就要特别小心后续所有命令里的引号。5.3 误删与恢复我的血泪史有一次我写了一个cm delete 3命令因为调试时测试直接把一条重要任务从 inbox.md 里删了。当时没有确认提示也没有备份等我发现时已经过了半天。后来我把整个CAVE_HOME目录纳入 Git 仓库管理。每次操作后自动提交或者至少每天提交一次cd ~/Documents/cave git add -A git commit -m daily backup这样任何误删都能通过git log和git checkout找回。这是我最强烈推荐的做法。本质上caveman 的文件是文本Git 天生就是为追踪文本变更设计的。你不需要理解 Git 原理只需记住一个命令组合。如果你不会 Git退一步用文件历史或云同步的“恢复到之前版本”功能也行。但 Git 的优势是它完全本地不受任何服务商限制。提示如果你用的是 Git建议给仓库加一份.gitignore忽略临时文件比如*.tmp和.DS_Store。这样每次提交只会包含真正有意义的内容。5.4 跨平台使用macOS / Linux / Windows我是 macOS 主力Linux 服务器偶尔用Windows 主要跑测试。在实际切换中发现caveman 在 macOS 和 Linux 上几乎零差异Windows 上有几个坑终端语法不同。PowerShell 下$HOME能识别但别直接照搬export建议用 Git Bash 运行能统一 shell 语法。换行符问题。Windows 写文件默认\r\n可能让 grep 匹配^时出问题。建议在脚本中统一用newline或写入时把换行符转成\n。文件名大小写敏感度。macOS 默认大小写不敏感Linux 敏感。如果你在 Linux 上写Inbox.md再在 macOS 上找inbox.md可能会找不到。统一用小写文件名是最省事的方式。解决这些问题的核心技巧是不要在一台机器上做太多“本地优化”尽量让脚本逻辑只依赖标准库。这样跨平台时基本不用改。我自己是在 Linux 上测试完脚本然后在 macOS 和 Windows 上各自跑一遍冒烟测试确认 add、list、done、archive 四个主流程能走通即可。6. 避坑心得与扩展玩法6.1 三条避坑原则写了这么久总结几条我用下来最真切的建议第一不要把 caveman 变成第二套复杂系统。不要加字段、加插件、加颜色高亮。工具的核心价值是“薄”它一旦开始变厚就失去了存在的意义。我在实际使用中发现自己总想加“提醒时间、子任务、循环任务”最后都忍住了。真正需要提醒的事交给系统日历。第二每天必须做一次“清理”。早上倒空大脑中午整理 inbox晚上 archive。如果不定期清理inbox 会累积到几十行caveman 的优势就消失了。我把它当作一个仪式类似程序员下班前的 git commit 和 code review。只要保持这个节奏整个系统就是闭环的。第三让文件最终走向“可读性大于一切”。你写下的任务终有一天要回看。所以宁可少写一点“为了机器而格式化”的内容也要写成一句正常人看得懂的话。比如“和产品确认新版本验收标准”比“prod-v2-验收-check”这种缩写好一百倍。文本工具的终极优势是给人看而不是给机器看。6.2 扩展成家庭记录系统caveman 虽然是为任务管理设计的但它的数据模型是“文件 行”所以稍微换个思路就能变成其他系统。我自己就顺手做了两个变体一个是家庭备忘系统。把inbox.md改成购物清单.md把today.md改成本周安排.md本质上就是一个全家共享的文本告示板。家里人只需要打开指定文件甚至不需要命令行任何编辑器都能编辑。这正好体现了纯文本的可移植性。另一个是阅读记录系统。用- [ ] 《书名》 读到第100页这种格式管理待读和进度。配合cm archive每周归档成一个“已读文件”。虽然听起来很寒酸但对一个“记录读过什么而不是管理知识库”的需求来说已经足够了。从这些例子可以看到caveman 的边界其实很模糊。它不是某个特定产品更像一种“用文本组织生活”的方法论。你完全可以把它的命令改改适配自己的场景。我之前还试过用它记录健身计划每周归档一次训练内容效果也不错。6.3 给想要“原始”的人的建议最后我想说如果你也想试试这种原始工具不要一上来就照搬我的全部配置。你可以先只用cm add和cm list这两个命令用一周。等习惯了再逐步引入cm push、cm archive再考虑 Git 备份。我个人在实际操作中的体会是caveman 教会我的不是“怎么管理任务”而是“怎么减少做决策的次数”。它把所有无关变量都删掉了只留下一行行文字和两个状态。每次打开终端看到的就是今天要做的几件事没有未读角标、没有提醒红点、没有几百个项目的切换按钮。这种朴素的界面反而让我更清楚下一步应该做什么。如果你最终用起来觉得不够“智能”那也是正常的。因为它本来就不是智能的它只是一个洞穴墙壁。但洞穴墙壁永远陪着你不会升级、不会崩溃、不会逼你续费。这就够了。