前言¶
接手陌生代碼庫,常見做法是讓模型直接掃源碼:讀得慢,上下文喫緊,每次新會話還要從頭再來。另一種做法是讓模型順手寫一堆 Markdown,問題是「已同步」沒法證僞——沒有清單可覈對,改了什麼全靠寫文檔那一次輸出自己說。
下面介紹的 proj-doc-builder 走第三條路。工具負責結構:目錄大綱、內容哈希基線、發佈事務;正文由人和模型寫;加載時由獨立腳本先校驗 manifest,再交給模型。作者 walvka-code,倉庫在 walvka-code/proj-doc-builder,以 DeepSeek Harness bundle(@walvka-code/proj-doc-builder)和普通 CLI 兩種形態發佈,歸類爲工作流插件。
它解決什麼¶
README 把問題拆成兩條。
- 要快速梳理陌生代碼庫,還要留下「後續改了什麼」的基線。插件爲每個源碼目錄維護一份 Markdown 大綱,內容是文件清單、關鍵符號、依賴關係、該目錄在系統中的角色;同時用
_scan_state.json記住上次同步時源碼的樣子,下次同步只處理真正變化的目錄。 - 要讓新起的 AI 對話直接拿到工程結構。加載器
tools/load_docs.py先校驗 manifest 與磁盤文檔逐份一致,再輸出概覽(目錄樹、元數據、小節名)或單獨某份文檔。寫側和讀側共用一份契約,不會各寫一套解析而漂移。
它的立場寫在 README 開頭:文檔「已同步」必須由工具寫下的 manifest 背書,而不是由生成它的模型自述。理由也直接——生成者與校驗者必須是兩個執行體,否則那句「已同步」永遠不能被證僞。
核心保證¶
README 列了七項機制,針對的是批量更新文檔時常見的崩點。
- 默認零寫入:不帶
--apply一律只報告,staging 放在系統臨時目錄。 - 單一提交點:
project_docs/_manifest.json的那一次原子替換是文檔代際的唯一 commit point。 - 不覆蓋併發寫入:發佈走 no-replace rename,Windows 是
os.rename,Linux 是renameat2(RENAME_NOREPLACE)。 - 失敗必須分類:只報
NOT_INSTALLED/INSTALLED/UNKNOWN/PARTIALLY_INSTALLED,不會虛構「已提交」。 - 不污染工作副本:鎖、事務 journal、CAS 產物都在臨時目錄。
- 刪除是舉證而非清理:只有源碼目錄確認消失、零入站引用、帶精確 marker 且已註冊時才產生隔離候選,默認只報告。
- 冪等:零變化運行輸出
planned_writes == [],不刷新last_synced。
安裝¶
插件提供三種安裝方式,以下命令照錄 README 原文。
方式一,DSH bundle,推薦路徑。在 Plugins → More → Custom plugin markets → Discover 里加入市場索引地址:
https://walvka-code.github.io/proj-doc-builder/index.json
同一份索引也發佈在 https://raw.githubusercontent.com/walvka-code/proj-doc-builder/main/index.json,但部分網絡無法直連該域名,以 GitHub Pages 地址爲準。不想用市場可以釘住某個 commit,bundle 是倉庫裏的一個包目錄:
git+https://github.com/walvka-code/proj-doc-builder.git#<40 位 commit>
也可以用 plugin_manager 裝本地檢出:
{ "action": "install_bundle", "target": "C:/path/to/proj-doc-builder" }
方式二,當項目技能用。把 skills/proj-doc-builder/ 複製到 <project>/.dsh/skills/proj-doc-builder/,SKILL.md 按宿主報告的技能目錄解析工具路徑,兩種佈局都成立。
方式三,當普通 CLI 用,不依賴 agent 框架:
git clone <this-repo> proj-doc-builder
SKILL="$(pwd)/proj-doc-builder/skills/proj-doc-builder"
python "$SKILL/tools/sync_project_docs.py" /path/to/your/project --dry-run --json
運行條件來自 README:Python 3.12+,運行時只用標準庫;測試需要 pytest,見 requirements-dev.txt。bundle 聲明來自 package.json:node >= 20,dsh >= 0.2.1-rc.5。項目側前置條件是根目錄有 project_docs/ 和 CLAUDE.md 兩個入口。
典型用法¶
兩個腳本分工明確:sync_project_docs.py 生成和發佈索引,load_docs.py 給新對話提供入口。先 dry-run 看變更、確認後 --apply,是 README 給出的固定姿勢:
# 技能目錄:SKILL.md 和 tools/ 都在這裏
SKILL="$(pwd)/skills/proj-doc-builder"
# 同步文檔索引:先看變更,確認後再發布
python "$SKILL/tools/sync_project_docs.py" . --dry-run --json
python "$SKILL/tools/sync_project_docs.py" . --apply --json
# 給新對話一個入口:先概覽,再按源碼目錄定位
python "$SKILL/tools/load_docs.py" . --json
python "$SKILL/tools/load_docs.py" . --mode target --target src/core --json
加載器的取用粒度更細:
# 全部文檔、只要元數據
python "$SKILL/tools/load_docs.py" . --limit 0 --sections 0 --json
# 定位到一份文檔(源碼目錄、文檔路徑、工程相對路徑都可)
python "$SKILL/tools/load_docs.py" . --mode target --target project_docs/src/core.md --json
# 只取某一份裏的某一節
python "$SKILL/tools/load_docs.py" . --mode target --target src/core --section 依賴關係 --json
加載器的核心是先校驗再加載:manifest 與磁盤文檔不一致,比如被改動過、多出未註冊文檔,就以退出碼 1 報 manifest-stale,不會拿陳舊索引去服務模型。生成側退出碼:0 全過,1 閘門失敗(含存在待恢復事務),2 輸入或運行錯誤,3 發佈失敗。3 不等於未發佈,以 publish.disposition 爲準。
工作流與數據模型¶
生成過程分六個階段,協調器不生成自然語言,正文由生成方寫,它只做校驗和事務發佈。
- 預檢:確認項目根有
project_docs/和CLAUDE.md,無待恢復事務。 --dry-run得到變化分類和工作清單,鍵是scan.worklist.scaffold。- 生成方按清單逐份寫正文,各份過
--strict校驗。 - 全局 dry-run 閉環,做斷鏈、阻斷項和 manifest 覆蓋面檢查,要求
validation.blocking_failures == []。 - 退役報告:列出源碼已消失且零引用的文檔,默認只報告。
--apply發佈,產出 manifest、CLAUDE 文檔樹和 scan state,終驗要求planned_writes == []。
數據落在三個位置。project_docs/_manifest.json 是 schema v2,每份文檔記錄 path、depth、title、status、bytes、sha256、synced_at,另給 generation_id。project_docs/_scan_state.json 是源碼目錄快照,README 直接稱它爲「改動跟蹤基線」。CLAUDE.md 有六組 marker,工具只替換第一組 doc-tree-instruction,其餘只校驗 marker 唯一、成對、不交疊。
平臺與測試¶
平臺支持按 README 表格:Windows 已驗證,說明提到重解析點與 junction 識別、句柄級刪除、msvcrt 鎖;Linux 已驗證,說明提到 fcntl 鎖、逐級 O_NOFOLLOW、renameat2 和目錄 fsync;macOS 未驗證,有 renamex_np 分支但沒有 CI。兩處細節在原文裏被單獨強調。
Linux 上 renameat2(RENAME_NOREPLACE) 是硬依賴,不可用時拋 NoReplaceRenameUnsupported,明確拒絕回退到會覆蓋的 rename。目錄 fsync 被拒時降級但不中斷,報告暴露 durability-degraded,真實 I/O 錯誤仍上拋。
測試運行方式:
python -m pytest skills/proj-doc-builder/tests -q
README 寫有 360 個用例,Windows 上 354 passed、6 skipped,全部用 tmp_path,不觸碰真實 project_docs/。用例鎖定的不是功能而是崩潰一致性:manifest 之前失敗必須完整回滾,之後失敗只准報 installed / partial / unknown;覆蓋僞造 journal 槽位、ABA 競態、staging 期間文檔被併發修改。
取捨也寫在 README 裏:事務 journal 和 CAS 產物目錄必須與項目根同一文件系統,跨盤會讓 --apply 直接拒絕;路徑只接受相對 POSIX、小寫 .md,拒絕絕對路徑、盤符、反斜槓、..、ADS、保留設備名和大小寫衝突;不跟隨 symlink、junction、reparse point;--full 只是掃描器的 force_full 開關,不等於逐份盤點文件清單。
適用場景與注意¶
適合這類場景:接手陌生代碼庫,需要快速建立結構認知;希望新起的 AI 會話直接加載工程畫像;希望文檔同步能進 CI,且結論由 manifest 提供,不靠模型自述。
幾條提醒。插件以當前 DSH 進程權限運行,發佈文檔和替換 CLAUDE.md marker 都是寫盤動作,安裝前建議打開源碼和許可證覈對行爲。許可證是 MIT。需要釘版本或離線分發時,用 git+https://... #<commit> 或本地 checkout,不要依賴市場地址的可用性。macOS 上行爲未經驗證。文檔退役是舉證流程而非清理工具,默認只報告。
結尾¶
proj-doc-builder 解決的不是文檔不夠多,而是文檔無法被證僞。它給出的三樣東西都寫在文件裏:一份工具寫入的 manifest、一份內容哈希基線、一次原子發佈的事務邊界。換來的是每一次「文檔已同步」都能被獨立複覈,而不需要信任寫文檔的那一方。
- 插件目錄頁:https://www.skillhub.cn/plugins/walvka-code/proj-doc-builder
- GitHub:https://github.com/walvka-code/proj-doc-builder