☰
极简纯文本任务管理CLI:caveman的设计与实现
2026/10/29 8:17:48 网站建设 项目流程

前阵子整理电脑里的旧项目,翻出一个叫caveman的小工具。名字是我当时随手起的,本意是"像穴居人一样简单直接",用来管日常任务的。结果这个小工具在我电脑上一用就是一年多,从最早的三十行 shell 脚本,慢慢长成了一个不到两百行 Python 的极简任务管理 CLI,每天陪我处理工作上的琐事。今天把它完整的实现思路和踩坑过程整理出来,分享给同样被各种重量级任务管理工具折腾得够呛的朋友。

这个工具能做什么,一句话说清:它在终端里维护一份纯文本任务清单,只提供五个命令——添加任务、看当前焦点、完成任务、查历史记录、看统计——其余什么都不做。它适合的是那些不想被花哨功能牵着走、希望任务管理回归"写下来、做掉、划掉"这三步的人。如果你手头已经有一套复杂的管理流程,但总觉得维护清单本身比做事情还累,那这篇文章应该能给你一些启发。

1. 项目由来:被Todoist和Notion的花哨功能逼出来的

先说背景。我之前是 Todoist 的重度用户,后来又试过 Things、TickTick,还在 Notion 里搭过一套任务数据库。工具换了好几轮,功能确实越来越强——标签、优先级、截止日期、子任务、提醒、看板视图、跨平台同步,应有尽有。但时间长了发现一个很尴尬的事实:我每天花在"维护任务清单"上的时间,比真正做任务的时间还多。

典型场景是这样的:早上打开 Todoist,看到三十几个任务,每个都有优先级和标签。我得先想今天做哪几个,于是开始排序、改日期、拖拽。好不容易列完今天的计划,中间来了个新需求,又得想该放哪个项目、什么截止日期、要不要加个提醒。等折腾完了,半小时没了,正事还没开始。

后来看到一种说法,叫"任务管理工具越复杂,你完成任务的概率越低"。想想确实有道理。任务管理本质上是给大脑减负,但如果工具本身就要求你不间断地做决策——这个任务放哪、什么时候做、什么优先级——那它其实是在给你增负。

还有一次 Notion 的同步出了问题,我那个写满任务的数据库打不开了。当时我整个人是懵的,因为很多想法只存在那个数据库里,离开它我完全不知道这段时间自己该做什么。也是从那次开始,我下定决心:工具可以简单,但数据必须掌握在自己手里。

于是决定自己写一个。需求列得很克制:

  • 只有一个任务列表,不做项目、标签、子任务这些概念
  • 每次只让我聚焦一件事,而不是面对一整个看板
  • 数据是纯文本文件,任何编辑器都能打开,丢了也能凭文件名想起来
  • 所有操作三秒内完成,不加任何动画、弹窗、确认框

名字就叫caveman。原始、直接、靠得住。

2. 设计理念:像穴居人一样聚焦眼前的一件大事

2.1 "当前焦点"才是核心概念,而不是"任务列表"

大多数任务管理工具的核心抽象是"任务",于是你天然会往里面塞越来越多东西:一百个任务、一百个优先级、分了五个项目。但人的一天能高质量完成的事其实就那么几件,尤其是需要深度投入的工作。

所以caveman的核心抽象不是"任务",而是**"当前焦点"**。它只管理一个问题:此时此刻,我唯一应该做的那件事是什么。

这个设计模仿了野外生存的逻辑:穴居人不会同时盯着一群猎物,他只会盯住眼前最近的那一只。一次只追一只兔子,比幻想围住一群猛犸象实在得多。

对应到命令上就是:caveman add往清单里丢一个新任务,caveman now把某个任务设为当前焦点,caveman done完成它。没有"稍后处理"、没有"推迟到下周"、没有"按项目过滤"。当你在几个任务之间来回摇摆的时候,now命令会直接告诉你该干嘛。

2.2 为什么数据用纯文本,不用 SQLite

这是被问得最多的一个问题。我的回答:因为 SQLite 虽然好用,但它是个"盒子"。数据进去了,你不用专门工具或者写 SQL 就看不到它。但纯文本不一样,它可读、可 grep、可 diff、可 pipe 给其他命令,一门语言一种工具,随时随地能处理。

举个具体例子:有一次我想统计一下自己过去一个月完成了多少任务,SQLite 方案得写一条带日期函数的查询语句;而纯文本方案我直接一个grep -c "done" ~/.caveman/tasks.txt就搞定了。再比如同步,SQLite 文件同步到不同设备上容易出并发问题,但一个文本文件,丢到任何网盘或者用scp同步,最多就是最后写入的覆盖前面的,格式永远不会坏。

当然纯文本也有代价,比如不支持复杂查询、不好做并发控制。但对于"每天记录几条任务"这个场景,这些代价完全可接受。好的极简设计不是"功能少",而是功能少到不会让系统崩坏、让用户受困。

2.3 任何操作不超过三秒

我给自己定了一个硬性指标:任何一个caveman操作,从敲下命令到看到结果,不超过三秒钟。包括打开终端、敲caveman add 写周报、回车、看到确认信息。这要求工具启动时间足够短、命令语义足够直观、输出足够简洁。

启动时间这个好解决,Python 标准库脚本,启动基本感觉不到延迟。命令语义方面我做了不少细节工作。比如caveman now既可以查看当前焦点,也可以把某个任务设为焦点:

caveman now # 查看当前焦点任务 caveman now 3 # 把列表中第 3 个任务设为焦点

再比如caveman done,直接不带参数就是"完成当前焦点任务",带任务编号就是"完成指定任务":

caveman done # 完成当前正在做的这件事 caveman done 2 # 完成第 2 个任务

这样设计的好处是:大多数时候我只需要敲最少的字,剩下的交给工具推断。人的决策负担降到了最低——完全不需要思考该用哪个参数组合。

3. 核心实现:一个纯标准库的 Python CLI 是怎么落地的

3.1 环境准备与整体结构

caveman用 Python 3.9+ 实现,不依赖任何第三方库。这样做的好处是部署极其简单:任何装有 Python 3 的机器上,把脚本文件拷过去就能跑。我在一台只装了系统自带 Python 的旧笔记本上测试过,完全没问题。

文件结构很简单:

caveman/ ├── caveman.py # 主入口,约 180 行 └── README.md # 使用说明

数据文件默认放在~/.caveman/tasks.txt,首次运行时自动创建。里面每一行是一个任务的明文记录。

3.2 数据格式:让排序和时间戳友好是核心

任务存储的格式直接决定了后面所有功能的复杂度。一开始我随手写成了任务描述 | 状态 | 时间戳,后来踩了坑(后面细说),改成了一种更适合排序和解析的固定格式:

242|2024-03-18 09:30|2024-03-18 11:20|完成caveman的README |done 241|2024-03-18 09:00|2024-03-18 09:25|梳理博客发布清单 |done 240|2024-03-18 08:50|2024-03-18 08:50|回应技术交流群里的问题 |done 239|2024-03-18 08:20| |给caveman写测试用例 |focus 238|2024-03-17 22:10|2024-03-17 22:30|整理caveman的设计笔记 |done

字段顺序:ID | 创建时间 | 完成时间(空则未完成) | 任务描述 | 状态。这样的格式有几个好处:

  • 用|做分隔符,任务描述里的逗号、空格都不用转义
  • YYYY-MM-DD HH:MM的时间格式可以按字典序直接排序,不用转成时间对象
  • 状态字段done/focus/pending直接 grep 就能筛出各类任务
  • 完成时间是固定的第 3 列,未完成的任务该列为空,一眼能看出哪些没做完

3.3 五个核心子命令的实现

主程序用argparse分发子命令。整体框架初始化时先确保数据文件存在,然后加载所有任务到一个列表里:

#!/usr/bin/env python3 """caveman - 一个极简的纯文本任务管理工具""" import argparse import os import sys from datetime import datetime DATA_DIR = os.path.expanduser("~/.caveman") DATA_FILE = os.path.join(DATA_DIR, "tasks.txt") TIME_FMT = "%Y-%m-%d %H:%M" def ensure_data_file(): """确保数据目录和文件存在,不存在则创建。""" os.makedirs(DATA_DIR, exist_ok=True) if not os.path.exists(DATA_FILE): open(DATA_FILE, "w").close() def load_tasks(): """从数据文件加载全部任务,解析失败的行直接跳过。""" tasks = [] with open(DATA_FILE, "r", encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue parts = line.split("|") if len(parts) >= 5: tasks.append({ "id": int(parts[0]), "created": parts[1], "finished": parts[2], "desc": parts[3], "status": parts[4], }) return tasks def save_tasks(tasks): """原子写回任务列表,先落临时文件再 rename,防并发损坏。""" tmp_file = DATA_FILE + ".tmp" with open(tmp_file, "w", encoding="utf-8") as f: for task in tasks: f.write(f"{task['id']}|{task['created']}|{task['finished']}|" f"{task['desc']}|{task['status']}\n") os.replace(tmp_file, DATA_FILE)

save_tasks里用了"先写临时文件再替换"的原子写策略,这个后面讲并发问题的时候会细说。

再来看add命令的实现,它最长但逻辑最简单:

def cmd_add(args): tasks = load_tasks() now = datetime.now().strftime(TIME_FMT) new_id = (max(t["id"] for t in tasks) + 1) if tasks else 1 tasks.append({ "id": new_id, "created": now, "finished": "", "desc": " ".join(args.text), "status": "pending", }) save_tasks(tasks) print(f"已添加任务 #{new_id}: {' '.join(args.text)}")

注意几点:新任务的 ID 取当前最大 ID 加一,而不是用列表长度——这样删除任务后 ID 不会混乱;状态默认pending,可以通过--focus参数在添加时直接设为焦点:

def cmd_add(args): # ... if args.focus: tasks[-1]["status"] = "focus" # ...

重点说now命令,这是整个工具的灵魂。查看焦点时只显示状态为focus的任务;设置焦点时要把当前已有的焦点任务重置为pending,只把指定任务置为focus:

def cmd_now(args): tasks = load_tasks() if args.num is None: focused = [t for t in tasks if t["status"] == "focus"] if focused: t = focused[0] print(f"当前焦点: #{t['id']} {t['desc']} (自 {t['created']} 开始)") else: print("当前没有焦点任务。用 'caveman now <编号>' 设置一个。") return target = next((t for t in tasks if t["id"] == args.num), None) if not target: print(f"找不到任务 #{args.num}", file=sys.stderr) sys.exit(1) for t in tasks: if t["status"] == "focus": t["status"] = "pending" target["status"] = "focus" save_tasks(tasks) print(f"当前焦点已切换为: #{target['id']} {target['desc']}")

写到这里想补充一个设计原则:工具的命令要符合直觉,不要给用户太多的"记忆负担"。比如caveman now不带参数查、带参数设,这个语义学过 Linux 的cd命令的人都能秒懂。我不是凭空发明的,而是沿用了很多 Unix 工具"无参数显示默认信息,有参数操作指定目标"的惯例。

done命令的逻辑和now对称,唯一特殊的地方是支持--at参数补录完成时间:

def cmd_done(args): tasks = load_tasks() if args.num is None: target = next((t for t in tasks if t["status"] == "focus"), None) if not target: print("当前没有焦点任务可完成", file=sys.stderr) sys.exit(1) else: target = next((t for t in tasks if t["id"] == args.num), None) if not target: print(f"找不到任务 #{args.num}", file=sys.stderr) sys.exit(1) target["status"] = "done" target["finished"] = args.at if args.at else datetime.now().strftime(TIME_FMT) save_tasks(tasks) print(f"任务已完成: #{target['id']} {target['desc']}")

log和stats面向回顾场景。log默认打印最近 10 条任务,加--all输出全部;stats统计今天完成的任务数和所有任务的总数。这两个命令设计得特别简单,因为回顾只需要大致感知趋势,不需要数据透视表:

def cmd_log(args): tasks = load_tasks() recent = tasks[-10:] if not args.all else tasks for t in reversed(recent): mark = {"done": "[x]", "focus": "[*]", "pending": "[ ]"}.get(t["status"], "[ ]") finish = f" -> {t['finished']}" if t["finished"] else "" print(f"{mark} #{t['id']} {t['desc']} (创建于 {t['created']}{finish})") def cmd_stats(args): tasks = load_tasks() today = datetime.now().strftime("%Y-%m-%d") done_today = sum(1 for t in tasks if t["status"] == "done" and t["finished"].startswith(today)) print(f"任务总数: {len(tasks)},今日完成: {done_today}," f"当前焦点: {sum(1 for t in tasks if t['status'] == 'focus')}")

最后是main函数和解析部分,把子命令映射到对应的处理函数上:

def main(): parser = argparse.ArgumentParser(description="caveman - 极简纯文本任务管理") sub = parser.add_subparsers(dest="command", required=True) p_add = sub.add_parser("add", help="添加新任务") p_add.add_argument("text", nargs="+", help="任务描述") p_add.add_argument("--focus", action="store_true", help="添加后立即设为当前焦点") p_now = sub.add_parser("now", help="查看或设置当前焦点任务") p_now.add_argument("num", nargs="?", type=int, default=None, help="任务编号") p_now.set_defaults(func=cmd_now) p_done = sub.add_parser("done", help="完成任务") p_done.add_argument("num", nargs="?", type=int, default=None, help="任务编号") p_done.add_argument("--at", default=None, help="完成时间,格式 YYYY-MM-DD HH:MM,默认当前时间") p_done.set_defaults(func=cmd_done) p_log = sub.add_parser("log", help="查看历史任务") p_log.add_argument("--all", action="store_true", help="显示全部任务") p_log.set_defaults(func=cmd_log) p_stats = sub.add_parser("stats", help="查看统计") p_stats.set_defaults(func=cmd_stats) args = parser.parse_args() ensure_data_file() args.func(args) if __name__ == "__main__": main()

整个工具加起来差不多 180 行。虽然简单,但它照顾到了我日常使用的全部场景:记下来、聚焦、做完、回顾。没有一处多余的设计。

3.4 为什么用 Python 而不是 Shell 或 Go

有人可能会问,这么简单的工具直接写 shell 脚本不就行了?说实话,最初版本确实是 shell 脚本,但很快就发现两个问题:一是参数解析不好做,尤其是add命令后面跟的文本可能带空格和特殊字符;二是数据解析和格式化用 shell 的文本处理很别扭,维护起来容易出错。

Python 标准库在这个场景下是甜点区:argparse处理命令行参数成熟可靠,os.replace做原子写天然支持,字符串处理能力远比 shell 灵活。又不需要第三方依赖,部署还是拷文件就完事,跟 shell 脚本的便利性相比没差多少。

Go 编译成单个二进制文件也很吸引人,但对这个场景来说有点重了。Python 启动速度完全够用,而且我在 Python 里改代码不需要重新编译,改完直接跑,迭代快很多。选型的时候不能只看技术先进性,还要看迭代效率和够不够用。150 行的小工具,Python 就是最舒服的选择。

4. 真实场景下的用法:我的日常工作流

4.1 一天从"清空大脑"开始

我现在的习惯是每天早上的第一件事,打开终端执行caveman log,把脑子里惦记的事情全部快速记下来,不带任何筛选:

$ caveman add 给数据库写迁移脚本 $ caveman add 确认周五发布会的演示环境 $ caveman add 回复老王的邮件 $ caveman add 整理这个月的报销单

用完发现脑子里一下清爽了。那些反复盘旋的"别忘了"有了一个外部容器接住,大脑的内存被释放出来。

然后挑一个今天最重要、最应该先做的事情:

$ caveman log [ ] #132 给数据库写迁移脚本 (创建于 2024-03-18 08:20) [ ] #133 确认周五发布会的演示环境 (创建于 2024-03-18 08:21) ... $ caveman now 132 当前焦点已切换为: #132 给数据库写迁移脚本

这一系列操作下来不到 20 秒,但效果很好。以前我在 Todoist 上光是设置今天的计划,就要拖拽、选优先级、调时间,起码五分钟。现在反而是"随便记,然后挑一个出来干"这个动作最自然。

4.2 强制"单任务模式"的机制设计

now命令的一个关键细节是:设置新焦点时,旧的焦点会自动失去焦点状态。这保证了系统里任何时刻最多只有一个focus任务。

这个机制挺重要的。以前用看板工具,我习惯把四五件事都标成"今天做",结果真正做的时候还是很焦虑,因为哪怕摆在"今天做"一栏,还是不知道该先做哪个。现在caveman强制我选一个,选完其他任务在视觉上就淡出了,心理负担直接少一大半。

执行过程中来新任务了?有一个专门的"收集篮"动作,快速记下来,然后继续手头的事:

$ caveman add 下周的小组周会改到周三 已添加任务 #139: 下周的小组周会改到周三

注意这个操作不会打断当前焦点。add命令不管任务状态,新任务默认只是pending,我的焦点任务还是稳稳地保持着。这个设计是为了避免"记一件事"跟"做一件事"混在一起。收集篮和当前焦点是两回事,工具里也是两套命令。

4.3 收尾时用"回顾"替代"愧疚"

下班前我会做两个动作:完成的任务标记done,没完成的不做任何处理,然后看一眼今天的完成情况:

$ caveman done 任务已完成: #132 给数据库写迁移脚本 $ caveman stats 任务总数: 139,今日完成: 5,当前焦点: 1

以前用复杂工具的时候,每天晚上打开看板看到一堆没完成的"今天"任务,会产生很强的挫败感。现在这个顾虑小很多,因为caveman压根没有"逾期"这个概念——任务没完成只是停留在列表里,等明天重新挑焦点就好,不会有红色的过期标识提醒你失败了。

这个心理差异直接改善了我的使用习惯:任务管理工具不能只扮演监工的角色,它得能让你明天还想打开它。我认识的一些朋友就是被工具的惩罚机制搞到彻底放弃整理任务的。

4.4 与 Shell 的深度集成:让焦点始终可见

既然常驻终端,就得让工具融入终端本身。我加了一个 shell 集成功能,把当前焦点任务放到 shell 提示符里,这样每敲一条命令都能看到自己正在做什么:

# 在 ~/.bashrc 或 ~/.zshrc 里加入 caveman_prompt() { local focus focus=$(caveman now 2>/dev/null) if [[ -n "$focus" && "$focus" != "当前没有焦点任务"* ]]; then echo "(${focus#当前焦点: })" fi } PS1="\u@\h:\w \$(caveman_prompt) \$ "

效果类似这样:

user@host:~/projects $ (给数据库写迁移脚本) git status

这个集成是一次很自然的演进——我没有专门做"专注模式",只是让当前焦点信息以低干扰的方式出现在视野里。现在每次打开终端扫一眼提示符右侧,就清清楚楚知道自己接下来该干什么。工具的最高境界不是跑到你面前提醒你,而是安静地待在那里,需要它的时候一抬头就能看到。

5. 开发中踩过的坑:文本存储、并发写入与补录

5.1 两台终端同时操作,任务被静默覆盖

这是最早期踩的坑,也是直接导致save_tasks改成原子写入的起因。

当时把caveman部署到了办公电脑和家用电脑两台机器上,中间用网盘同步数据文件。有天下班在家里的电脑上添加了个任务,第二天到公司发现任务不见了。排查了一下,发现是这么个流程:公司的电脑开机时联网同步到了前一天旧版文件,而我在家用电脑上新加的任务被旧文件覆盖了。

这其实是所有基于同步盘的文本工具的通病。通用网盘对文件级同步是"整文件替换",不是数据库级的行合并。暴露的问题有俩,一是同步冲突,二是局部写入损坏。

针对前者,我能做的很有限——这是网盘同步的固有限制,除非自己搭冲突合并机制。针对后者,我能改的是确保任何一次写入都是完整的,绝不留下写到一半的文件:

def save_tasks(tasks): tmp_file = DATA_FILE + ".tmp" with open(tmp_file, "w", encoding="utf-8") as f: for task in tasks: f.write(...) os.replace(tmp_file, DATA_FILE)

先用临时文件写完整份数据,然后用os.replace原子替换目标文件。这样在任何时刻,磁盘上要么是旧文件,要么是新文件,不存在写一半的中间状态。至少不会因为写入中断导致整个数据文件变成一堆乱码。

不过说实话,多设备实时同步这个需求,用单文件存储的方案很难做到尽善尽美,只能在习惯上配合:重要的工作任务在离开一个设备前先caveman log --all确认一眼,再手动触发一次网盘同步。这也算是极简工具的用户不得不接受的代价——你用灵活性换来了简单,就得接受它在大规模同步场景下的短板。

5.2 时间格式没统一,导致数据排序错乱

第一个版本的时间字段格式是3/18 9:30这样。后来我发现按字符串排序时,10:00会排在9:00前面,因为'1'的字典序小于'9'。任务列表的打印顺序就开始乱了。

教训:时间数据永远不要用人类友好的格式来存储,存储格式必须对机器友好。改成YYYY-MM-DD HH:MM之后一切恢复正常——字典序等于时间序,根本不用解析成时间对象再排序。这个规则的通用性很强,写任何基于文本的数据格式都适用。

顺带说明一下为什么没有改成 Unix 时间戳。时间戳虽然排序也友好,但人眼直接读1700000000根本反应不过来时间。caveman的数据文件是要直接打开看的,用可读的日期时间格式能让人在不依赖任何工具的情况下理解内容。YYYY-MM-DD HH:MM兼顾了可读性和可排序性,是这个场景下的最优解。

5.3 补录需求:完成任务忘了打卡怎么办

用了一周之后发现一个新的使用痛点:经常干完一件事才想起来没执行caveman done。等我想起来的时候,任务已经做完半小时了,完成时间记录得不准确,"今日完成数"这个统计也就失真了。

补录功能必须安排上。但怎么设计才不违背"极简"原则?加交互式确认框肯定不行,那太啰嗦。最后采用的方式是给done命令加一个可选参数--at,允许手动指定完成时间:

$ caveman done 128 --at "2024-03-18 14:20" 任务已完成: #128 整理接口文档

完成时间默认取当前时间,只有明确需要补录时才传--at。而且--at参数的格式必须和存储格式完全一致,直接用YYYY-MM-DD HH:MM输入,不在工具内部做复杂的自然语言解析——这也是"简单"的代价,换来的是逻辑的确定性。

5.4 文件被误改导致整段崩溃的解析宽容性

文本文件有个特点:你自己可以随意编辑它,这是优点;但正因为可以随意编辑,偶尔会不小心改坏格式。有一次我在编辑器里操作时误删了一个|分隔符,保存后caveman直接抛异常退出,因为解析到那行时列数不对了。

处理方式是在load_tasks()里做了宽容解析——格式不完整的行跳过,而不是让整个程序崩溃:

def load_tasks(): tasks = [] with open(DATA_FILE, "r", encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue parts = line.split("|") if len(parts) >= 5: tasks.append({...}) else: # 格式不对,跳过并告警,但不中断 print(f"警告: 跳过格式异常的行: {line}", file=sys.stderr) return tasks

这样即使某行数据有问题,其他任务还能正常加载。配合前面说的原子写入,数据文件损坏的概率已经降到很低,但防御性编程还是值得做的。文本方案的优势是你可以随时手动修文件,劣势是人手一抖就可能把格式搞坏,所以工具的解析逻辑必须足够宽容。

另一个相关的习惯:每次大的结构改动前,先备份一下~/.caveman/tasks.txt。因为数据只有我自己掌握,丢了就是真丢了,没有任何云端的第二个副本。

6. 后续扩展:给吐槽它"太简陋"的人一个交代

很多人第一次看到caveman都会说:这也太简陋了,连个截止日期都没有,连个提醒都没有。我自己用下来反而觉得,对一些应用场景来说,"没有提醒"恰恰是特性而不是缺陷。因为提醒意味着工具会打断你,会制造"未读通知"的压力。caveman的理念是:任务躺在文件里,你主动去看它,它才变得重要;不看就说明它不是当前最重要的事,那也就不需要提醒。

不过有几个方向是我接下来确实准备加上去的,每个都仍然保持极简原则:

  • caveman review:每周日晚自动打印本周完成的任务清单,做一个简单的周回顾。不是数据分析,就是列出这周做完了什么,配上一句"这周辛苦了"。回顾的核心目的是给自己一个交代,不是做绩效评估。
  • caveman sync:考虑到多设备同步的痛点,考虑做一个显式的合并逻辑。原理很简单:不同设备各自维护本地文件,同步时以任务 ID 为键做并集合并,时间戳字段取较新的那个。文本格式让这个方案完全可行。
  • 与编辑器打通:加一个caveman edit直接用$EDITOR打开数据文件,让人可以快速批量修改。归根结底,数据是文本,编辑文本就是最高级的操作方式。

最后说点题外话。很多人问我,为什么要花时间写一个这么简单的东西,直接继续用某个现成工具不行吗?我的体会是:自己做工具最大的收获不是工具本身,而是你在设计过程中想清楚了什么功能对你重要、什么功能只是负担。这个思考过程本身比任何现成的任务管理方法都更有价值。你可以不写代码,但值得花五分钟想一下:我现在用的工具里,哪些功能是真正每天在服务的,哪些只是让我觉得自己在高效地管理生活?

caveman不会适合所有人。但如果你和我一样,受够了工具带来的维护成本,愿意把数据掌握在自己手里,那不妨试试这个思路——哪怕只写三十行 shell 脚本开始,也算迈出了一大步。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询