的極簡終端待辦工具caveman:把任務(wù)管理變回敲石頭)
1. 工具過載時(shí)代為什么我還想試試“穴居人”風(fēng)格的待辦工具1.1 復(fù)雜工具帶來的隱性成本我先說一個(gè)自己經(jīng)歷過的場(chǎng)景。有一陣子我為了“好好管理任務(wù)”手機(jī)里裝了三款待辦應(yīng)用電腦上還配了一個(gè)帶標(biāo)簽體系的桌面客戶端。剛開始確實(shí)新鮮又是設(shè)顏色又是分項(xiàng)目感覺自己的工作效率馬上就要起飛了。結(jié)果兩周之后我發(fā)現(xiàn)自己每天花在“整理任務(wù)清單”上的時(shí)間比“處理具體任務(wù)”還多——同步?jīng)_突要處理標(biāo)簽體系越分越亂各個(gè)平臺(tái)的通知來回轟炸。最諷刺的是我列了一堆“整理待辦工具”的任務(wù)它們就那樣躺在清單里和真正重要的事情混在一起然后一起過期。這就是復(fù)雜工具的隱性成本它把自己的維護(hù)成本悄悄轉(zhuǎn)移給了你而你誤以為那種“忙碌感”就是生產(chǎn)力。caveman 這個(gè)項(xiàng)目正是在這個(gè)背景下進(jìn)入我視野的。它自稱“穴居人”一共就三個(gè)命令數(shù)據(jù)存在本地一個(gè) JSON 文件里沒有賬號(hào)、沒有同步、沒有插件市場(chǎng)。第一眼看到它時(shí)我心想這也太簡陋了。但用了三周之后我反而把手機(jī)上的幾個(gè)待辦應(yīng)用全刪了。這篇文章就圍繞 caveman 展開。我會(huì)講它到底用什么思路做到了“簡單到像穴居人”怎么把它接入真實(shí)工作流實(shí)際使用中會(huì)遇到哪些坑以及它留給我的一些工具選型上的思考。如果你是那種被重型軟件折磨過、想找一個(gè)零負(fù)擔(dān)任務(wù)記錄方案的開發(fā)者或者你自己也有做一個(gè)小工具的想法這篇應(yīng)該能給你一點(diǎn)參考。1.2 caveman 對(duì)“簡單”的定義先說清楚caveman 的簡單不是“還沒開發(fā)完”的簡單而是“故意不做”的簡單。這一點(diǎn)很容易被誤會(huì)因?yàn)樗鳛橐粋€(gè)開源項(xiàng)目功能少得不像話——沒有優(yōu)先級(jí)沒有截止日期沒有提醒沒有子任務(wù)沒有云同步。你甚至不能給任務(wù)打標(biāo)簽。但它從一開始就把自己定位成“一塊石頭”——穴居人拿石頭砸堅(jiān)果不需要理解齒輪傳動(dòng)也不需要充電。所以它的核心邏輯被壓到了最低限度記錄一句話查看一句話標(biāo)記一件事干完了。僅此而已。這種取舍背后其實(shí)是一種很明確的姿態(tài)待辦工具的本質(zhì)價(jià)值不是幫你建立一套宏大體系而是讓你“把腦子里的事情騰出來”。你在終端里敲一行字它就記住了你不需要打開 App、跨過各種啟動(dòng)頁、處理各種彈窗只需要一個(gè)命令。如果你愿意你甚至可以把它當(dāng)成一個(gè)帶換行的便簽文件來用。我特別欣賞它對(duì)“數(shù)據(jù)”的態(tài)度。很多工具把數(shù)據(jù)鎖在自己的生態(tài)里導(dǎo)出要會(huì)員遷移要折騰。caveman 不一樣它的全部數(shù)據(jù)就是一個(gè)人類可讀的 JSON 文件安安靜靜躺在你的用戶目錄下。這意味著你能看見它、能備份它、能用任意腳本操作它甚至哪天整個(gè)工具不維護(hù)了你的數(shù)據(jù)也還是你的。這種“看得見摸得著”的安全感恰恰是現(xiàn)代軟件最稀缺的東西。2. caveman的存儲(chǔ)與命令設(shè)計(jì)一個(gè)JSON文件承載全部邏輯2.1 存儲(chǔ)機(jī)制解析caveman 把數(shù)據(jù)存成一個(gè) JSON 文件默認(rèn)路徑是用戶主目錄下的~/.caveman.json。第一次運(yùn)行工具時(shí)它會(huì)自動(dòng)創(chuàng)建這個(gè)文件之后每次執(zhí)行命令都是對(duì)這個(gè)文件的讀取和重寫。這里的核心邏輯非常簡單一個(gè)數(shù)組裝著所有待辦條目每條記錄有一個(gè)自增的 id、一段任務(wù)文本、以及一個(gè)標(biāo)記是否完成的狀態(tài)字段。一個(gè)典型的存儲(chǔ)結(jié)構(gòu)長這樣{ nextId: 5, items: [ { id: 1, text: 寫季度總結(jié), done: false }, { id: 2, text: 預(yù)定下周的會(huì)議室, done: true }, { id: 3, text: 給路由器換個(gè)位置, done: false } ] }nextId負(fù)責(zé)保證新任務(wù)的編號(hào)不會(huì)重復(fù)items就是任務(wù)主體。每次執(zhí)行操作后整個(gè)結(jié)構(gòu)會(huì)被完整寫回文件。這種全量讀全量寫的模式在任務(wù)量不大的場(chǎng)景下根本沒有性能問題反而極大降低了實(shí)現(xiàn)復(fù)雜度——不用考慮增量同步、不用維護(hù)索引、不用加鎖掛了一個(gè)文件就能跑。為什么選 JSON 而不是 SQLite 或者更復(fù)雜的存儲(chǔ)方案我的理解是作者從一開始就拋棄了“擴(kuò)展豐富性”這個(gè)目標(biāo)換取“人可以直接讀懂和修改”。SQLite 需要專門的客戶端工具才能查看而 JSON 用任何編輯器打開就能看懂甚至能直接手改。這一點(diǎn)對(duì)極簡工具來說太重要了它意味著即使工具本身掛了你也能無損取回所有數(shù)據(jù)。我自己就干過直接用 sed 替換文本、批量修改一批任務(wù)狀態(tài)的事情這種掌控感是重型應(yīng)用給不了的。2.2 三個(gè)命令的語義與邊界caveman 的全部功能就是三個(gè)動(dòng)作add、list、done。我把它們拆開看caveman add 任務(wù)描述追加一條新任務(wù)系統(tǒng)自動(dòng)分配一個(gè) id。這是唯一入口沒有富文本、沒有分類選項(xiàng)。caveman list展示所有任務(wù)及其編號(hào)和完成狀態(tài)。有的版本默認(rèn)展示未完成任務(wù)也有的版本全部展示不同分支行為略有差異但核心都是“讓你一眼看到現(xiàn)在還剩什么”。caveman done id根據(jù)編號(hào)把對(duì)應(yīng)任務(wù)標(biāo)記為已完成。完成之后任務(wù)不會(huì)消失它還在那個(gè) JSON 數(shù)組里只是done從 false 變成 true。這組設(shè)計(jì)最有趣的地方在于它的“邊界感”。它刻意不去做的事沒有“刪除”命令因?yàn)樽髡哒J(rèn)為標(biāo)記完成就夠了真錯(cuò)了文本里還留著痕跡沒有“修改”命令因?yàn)槿绻斿e(cuò)了你可以直接去編輯 JSON 文件。這種取舍乍看反直覺——用戶連基本的編輯能力都沒有但用久了你會(huì)發(fā)現(xiàn)絕大多數(shù)任務(wù)管理的需求其實(shí)就是“記下來、看一眼、劃掉”其余都是附帶動(dòng)作。少一條命令就少一分學(xué)習(xí)成本也少一個(gè)出錯(cuò)的地方。2.3 從源碼構(gòu)建與安裝caveman 用 Go 編寫這本身就非常符合它的氣質(zhì)的編譯出來是一個(gè)單一二進(jìn)制文件丟到 PATH 里就能用沒有任何運(yùn)行時(shí)依賴。安裝方式特別省事。如果你本地有 Go 環(huán)境直接編譯git clone caveman的倉庫地址 cd caveman go build -o caveman . sudo mv caveman /usr/local/bin/沒有 Go 環(huán)境的話也可以從倉庫的 Release 頁面直接下載對(duì)應(yīng)平臺(tái)的二進(jìn)制文件。更離譜的玩法是——因?yàn)樗壿嬏唵尉退隳奶靷}庫沒了你照著上面的 JSON 結(jié)構(gòu)自己用 Python 或者 Shell 寫一個(gè)等價(jià)腳本也花不了半小時(shí)。這大概是極簡設(shè)計(jì)能帶來的最奢侈的副產(chǎn)品隨時(shí)可以拋棄原版自己做一份。安裝完之后第一件事就是跑一下caveman list確保它能自動(dòng)創(chuàng)建配置文件。這一步驗(yàn)證的是權(quán)限和路徑有沒有問題如果當(dāng)前用戶對(duì)主目錄有寫權(quán)限基本不會(huì)出岔子。3. 從裝好到用順終端里的完整接入過程3.1 一個(gè)真實(shí)工作日的操作演示光說設(shè)計(jì)理念不如直接演示一遍。我把自己某天早上的實(shí)際流程還原一下每一步都在終端里敲你和我之間的工具使用方式應(yīng)該是一樣的。早上九點(diǎn)半到工位先看一眼今天要做什么caveman list如果昨天已經(jīng)加過一批任務(wù)這里會(huì)把未完成和一個(gè)簡單的序號(hào)一起打出來。然后開始干活中途接到新需求隨手記一筆caveman add 整理接口文檔下午三點(diǎn)前發(fā)給后端 caveman add 報(bào)銷發(fā)票兩張高鐵票 caveman add 聯(lián)系印刷廠確認(rèn)樣品進(jìn)度每加一條心里就少了一件需要記掛著的事。到中午吃完飯第一批任務(wù)處理完了回頭把對(duì)應(yīng)編號(hào)劃掉caveman done 3 caveman done 1再執(zhí)行一次caveman list剩下的就是還沒解決的。整個(gè)過程行云流水打開終端到退出可能只花了十秒鐘。它不會(huì)彈一個(gè)“今日達(dá)成率”也不會(huì)給你推送什么高分報(bào)告但完成了就是完成了那種踏實(shí)感比花里胡哨的統(tǒng)計(jì)有用得多。對(duì)我來說這個(gè)工具最舒服的地方在于它不打斷思路。你在終端里寫代碼時(shí)本身就是那種“命令行會(huì)話”的語境隨手敲一條任務(wù)比切到一個(gè)圖形化應(yīng)用、等它啟動(dòng)、再找一個(gè)輸入框要自然得多。caveman 就像一個(gè)一直蹲在你 shell 旁邊的洞穴伙伴你喊一聲它回應(yīng)一聲僅此而已。3.2 用alias和shell函數(shù)補(bǔ)齊體驗(yàn)官方的幾個(gè)命令比較精簡但日常使用中我還是給它加了一層自己的“殼”。畢竟工具可以極簡我自己的操作體驗(yàn)不用跟著極簡到生硬。首先最基礎(chǔ)的是起個(gè)別名把命令縮短到幾乎和白板筆一樣快alias ccaveman alias ctcaveman add alias clcaveman list alias cdcaveman done這樣一來記一條新任務(wù)就是ct 給客戶回電話確認(rèn)地址查看清單就是cl比敲完整命令省了一半時(shí)間。還有更進(jìn)一步的做法比如在 zsh 里定義一個(gè)函數(shù)帶個(gè)時(shí)間戳再取其中關(guān)鍵字function cag() { caveman add $(date %m-%d %H:%M) $* }這樣每次新增的任務(wù)會(huì)自帶一個(gè)時(shí)間字段你回頭翻 JSON 時(shí)能知道它是什么時(shí)候加進(jìn)去的。雖然工具有意不做“日期”概念但你在外面包一層就能輕松補(bǔ)上這就是所有文本化記錄工具的通用優(yōu)勢(shì)——一切可以靠外部組合來完成。我自己的 zsh 配置里還加了一條“今日總覽”的捷徑其實(shí)就是用一個(gè)函數(shù)包裝兩次調(diào)用function ctoday() { echo 待辦 caveman list echo 已完成 cat ~/.caveman.json | jq -r .items[] | select(.donetrue) | \(.id) \(.text) }這里調(diào)用了jq來手工解析 JSON屬于我個(gè)人的補(bǔ)充操作。它做不了什么“智能”但足夠讓一個(gè)極簡工具的觀感更接近“現(xiàn)代任務(wù)面板”。3.3 數(shù)據(jù)備份與多設(shè)備間的“人工同步”講到多設(shè)備這是很多人一聽“本地存儲(chǔ)”就會(huì)皺眉的地方家里一臺(tái)電腦公司一臺(tái)電腦怎么辦如果你期待無縫云同步那 caveman 確實(shí)不是給你的。但換個(gè)思路看它反而更忠實(shí)——文件就那一個(gè)任何現(xiàn)成的文件同步方案都能套上去完全不存在廠商鎖死的問題。我自己的處理方式是把它放進(jìn)一個(gè)私有 Git 倉庫兩臺(tái)機(jī)器各自克隆每天下班前git add .caveman.json git commit -m sync第二天到公司先git pull。如果哪天忘了也沒關(guān)系最多就是在某臺(tái)機(jī)器上冒出來一條老任務(wù)順手標(biāo)記完成就好。用過復(fù)雜同步工具的人都知道真正麻煩的是“沖突處理”和“多端狀態(tài)不一致”而 caveman 幾乎沒有這個(gè)煩惱因?yàn)閿?shù)據(jù)太簡單了即便發(fā)生覆蓋從舊備份里恢復(fù)也異常容易。如果你不想用 Git把它放到堅(jiān)果云、Dropbox 這類同步文件夾里也行。邏輯完全一樣——云端只是負(fù)責(zé)搬運(yùn)文件數(shù)據(jù)處理權(quán)力始終在你手里。所以每次有人問“這種工具沒有云同步是不是落后”我心里都在想依賴廠商的同步才叫落后一個(gè)隨時(shí)可以手動(dòng)復(fù)制的 JSON 文件才是永恒的兼容。4. 實(shí)際使用中繞不開的坑與補(bǔ)救方案4.1 文件損壞時(shí)的恢復(fù)經(jīng)驗(yàn)極簡工具的一個(gè)隱含代價(jià)是它不會(huì)為你做太多保護(hù)措施。JSON 文件如果被寫壞工具可能直接罷工甚至靜默丟失部分內(nèi)容。這事我遇到過一回當(dāng)時(shí)我一邊開著編輯器改~/.caveman.json一邊在另一個(gè)終端里執(zhí)行caveman add兩邊同時(shí)寫結(jié)果格式?jīng)_突整個(gè)文件損壞到無法解析。caveman list直接報(bào)錯(cuò)。這種情況下千萬不要慌亂第一個(gè)原則永遠(yuǎn)是“先備份再動(dòng)手”。如果你平時(shí)沒有做備份的習(xí)慣那么當(dāng)下最緊要的是把損壞文件復(fù)制一份出來再嘗試修復(fù)cp ~/.caveman.json ~/.caveman.json.bak cat ~/.caveman.json打開看之后大概率能發(fā)現(xiàn)問題常見的是逗號(hào)缺失、引號(hào)不成對(duì)、或者文件尾部被截?cái)?。修?fù)手段很直接用jq驗(yàn)證然后手工補(bǔ)齊。對(duì)于 JSON 這種格式只要結(jié)構(gòu)沒碎到離譜靠肉眼和數(shù)據(jù)片段就能恢復(fù)大半。如果文件已經(jīng)徹底亂掉那就只能以.bak或者之前的 Git 提交版本為準(zhǔn)損失一小段任務(wù)記錄換一個(gè)教訓(xùn)——以后任何對(duì)文件的直接編輯都先開一個(gè)臨時(shí)副本。經(jīng)過這次事件我給自己定了一個(gè)規(guī)則所有對(duì) JSON 的手動(dòng)修改都先復(fù)制成帶后綴的備份文件改完確認(rèn)工具讀通了再刪。這也是我反復(fù)建議任何使用文本存儲(chǔ)工具的朋友應(yīng)該做的一步。4.2 并發(fā)寫入與多窗口沖突caveman 的寫入模式是“讀出所有內(nèi)容改完再整個(gè)寫入同一個(gè)文件”。這種模式在單用戶單進(jìn)程場(chǎng)景下沒有問題但你要是同時(shí)打開兩個(gè)終端窗口各自執(zhí)行一條caveman add后寫入的那個(gè)進(jìn)程可能把前一個(gè)進(jìn)程的修改覆蓋掉。這類問題的本質(zhì)是“你沒法同時(shí)維持兩份對(duì)同一文件的修改”它是幾乎所有輕量文件型存儲(chǔ)工具的共性毛病并不只是 caveman 的問題。我的應(yīng)對(duì)方案其實(shí)很簡單養(yǎng)成“在一個(gè)窗口里集中操作”的習(xí)慣。反正一次會(huì)話中新增任務(wù)的頻率沒有那么高真需要搞“并發(fā)”就寫個(gè) Shell 層面的簡單串行包裝或者干脆直接向文件追加——因?yàn)?JSON 數(shù)組的寫法是[ {id:1,...}, {id:2,...} ]如果只是追加新元素你可以先把右括號(hào)前的]替換成, {...}]這個(gè)操作只要腳本寫得嚴(yán)謹(jǐn)就不會(huì)破壞前面內(nèi)容。不過說實(shí)話普通使用場(chǎng)景完全不需要做到這一步。我自己三周用下來真正因?yàn)椴l(fā)丟失數(shù)據(jù)的只有上述那一次手動(dòng)修改其他時(shí)候從沒出過問題。它畢竟不是一個(gè)多人在線的協(xié)作系統(tǒng)你把它當(dāng)成“個(gè)人備忘錄”來理解就不會(huì)覺得這是缺陷。4.3 缺失功能用外部管線補(bǔ)齊caveman 沒有提醒、沒有統(tǒng)計(jì)、沒有優(yōu)先級(jí)。這些在很多用戶眼里是不可接受的“缺陷”但換個(gè)角度看它們都是可以在工具外面補(bǔ)回來的功能。Unix 那句“每個(gè)程序只做好一件事”的理念在這里體現(xiàn)得淋漓盡致caveman 負(fù)責(zé)存儲(chǔ)和展示至于提醒交給 cron至于統(tǒng)計(jì)交給 wc 和 grep至于“今日重點(diǎn)”交給管道和過濾。舉一個(gè)實(shí)際例子。我想要每天早上九點(diǎn)自動(dòng)看到任務(wù)清單就在 crontab 里加了一行0 9 * * * caveman list /tmp/today.txt; head -5 /tmp/today.txt | mail -s 今日待辦 myemail.com在 Linux 桌面環(huán)境還可以直接彈系統(tǒng)通知用notify-send也一樣。想要統(tǒng)計(jì)“完成了多少任務(wù)”一行命令就能得到數(shù)字cat ~/.caveman.json | jq [.items[] | select(.donetrue)] | length如果敢于折騰你甚至能把這個(gè) JSON 文件喂給一個(gè)簡單的網(wǎng)頁腳本實(shí)現(xiàn)一個(gè)完全私有、完全自制的“可視化看板”。這個(gè)思路比要求 caveman 長出各種插件功能要聰明得多——它不背負(fù)擔(dān)你還拿了全部自由度真正的雙贏。5. 極簡工具的價(jià)值邊界caveman給我上的工具選型課5.1 和主流待辦工具的一次正面比較我身邊很多朋友聽說我在用一個(gè)“穴居人”工具第一反應(yīng)都是你圖什么為了讓他們明白我拉了一張對(duì)比表把 caveman 和幾個(gè)典型方案的差異擺明白對(duì)比維度cavemanTodoist / Microsoft To DoTaskwarrior配置成本近乎零需要賬號(hào)、平臺(tái)、設(shè)置需要學(xué)習(xí)命令體系功能豐富度極簡記錄、查看、完成高項(xiàng)目、標(biāo)簽、協(xié)作、日歷高依賴、期限、報(bào)告數(shù)據(jù)存儲(chǔ)本地 JSON隨時(shí)可讀云端數(shù)據(jù)庫導(dǎo)出受限本地文本文件有專門格式同步方式靠文件同步自己控制廠商云同步可配同步服務(wù)器但復(fù)雜學(xué)習(xí)曲線幾分鐘中等較陡適合場(chǎng)景個(gè)人輕量記錄團(tuán)隊(duì)協(xié)作與重度管理極客習(xí)慣、復(fù)雜工作流從這個(gè)表格能很清楚地看出caveman 錨定的位置是“一個(gè)人、一天、十幾件事”這種輕量需求。它不可能替代 Todoist 的團(tuán)隊(duì)共享能力也沒法跟 Taskwarrior 的完備度掰手腕。但如果你需要的只是“別把事忘了”它的性價(jià)比反而是最高的——不需要付費(fèi)不需要理解概念不需要被任何設(shè)計(jì)規(guī)則綁架。5.2 這類工具適合誰一份自測(cè)清單到底要不要用 caveman 這種工具我自己的判斷標(biāo)準(zhǔn)可以概括成下面幾個(gè)問題你每天需要管理的任務(wù)量是三五條、十幾條還是幾十條帶子任務(wù)的復(fù)雜項(xiàng)目你是否經(jīng)常在命令行環(huán)境里工作還是幾乎所有時(shí)間都泡在圖形化應(yīng)用里你更信任“一個(gè)自己看得見的文件”還是更喜歡“一個(gè)無所不能的 App 幫你安排一切”你能接受沒有提醒、沒有日歷、沒有倒數(shù)日嗎如果前三條的答案偏向“終端”“少量任務(wù)”“數(shù)據(jù)自控”那 caveman 會(huì)很合適如果有一天你需要協(xié)作、需要精細(xì)的周計(jì)劃、需要和日歷聯(lián)動(dòng)那確實(shí)該換用重型工具了。關(guān)鍵不在于誰好誰壞而在于你是否清楚自己的場(chǎng)景到底需要多少復(fù)雜度。很多人痛苦不是因?yàn)楣ぞ咛俣怯缅e(cuò)了復(fù)雜度的工具來承擔(dān)一個(gè)簡單任務(wù)。5.3 留給自研小工具的幾條經(jīng)驗(yàn)從 caveman 上學(xué)到最深刻的東西是它對(duì)于“產(chǎn)品的邊界”的克制。我平時(shí)偶爾也會(huì)寫一些小工具自用以前總想加這個(gè)功能加那個(gè)功能結(jié)果弄到一半就不想維護(hù)了。現(xiàn)在我做工具時(shí)堅(jiān)持三條原則很大程度上是受這個(gè)項(xiàng)目的啟發(fā)第一能用文本解決的問題就絕不引入專用數(shù)據(jù)庫。文本文件或 JSON 文件帶來的兼容性、可讀性、可遷移性是所有數(shù)據(jù)庫都給不了的。第二入口越少越好。一個(gè)任務(wù)工具如果有一百個(gè)參數(shù)、十個(gè)子命令讓用戶每次使用都要思考那它本質(zhì)上已經(jīng)背叛了“工具”的初衷。第三讓用戶永遠(yuǎn)能訪問底層數(shù)據(jù)。人只要知道自己存的東西在哪里、長什么樣、能不能帶走就會(huì)對(duì)這個(gè)工具有一種天然的信任這種信任是任何精美界面都換不來的。我把這三條經(jīng)驗(yàn)用到自己的一個(gè)筆記草稿腳本上之后整個(gè)項(xiàng)目的代碼量減少了近一半維護(hù)負(fù)擔(dān)也變得微不足道。工具的意義是幫助你、配合你、然后默默退到一邊而不是反過來成為需要你供養(yǎng)的另一個(gè)系統(tǒng)——caveman 用自己的方式把這句話證明了一遍。如果硬要說還缺點(diǎn)什么那就是它不太適合直接管理那些需要設(shè)定期限、按周復(fù)盤的重型任務(wù)遇到這種情況我依然會(huì)打開更正式的表格。但在絕大多數(shù)普普通通的一天里敲一行字、劃掉一件事這種穴居人式的樸素確實(shí)讓我省下了不少心力。最后提醒所有想試的朋友一句話先備份那個(gè) JSON 文件然后把它當(dāng)成一塊簡單的石頭用別指望它長出翅膀。