前言¶
每個項目都有自己的口癖:「不要寫無意義的強制類型轉換」「SQL 全部收斂進 model 層,service 和 handler 裏不準拼」「動手前先確認現有實現」。這類話每個新會話都要重說一遍,人多了就變成提醒成本。
常見的做法是在項目裏放 AGENTS.md / CLAUDE.md,但這會把約定帶進版本庫,同事的機器上照樣生效,偶爾並不代表整個團隊都同意。dsh-workspace-rules 走的是另一個方向:約定寫在 <項目>/CLAUDE.local.md 裏,這份文件是「本地私有」的,配合 .git/info/exclude 完全不進版本庫,同時由 DSH 內置加載器在新會話開始時自動讀取。插件本身只做一件事——給你一個設置面板去讀寫這個文件,不負責注入。
下面介紹這個插件,維護者是 winson-young,倉庫地址見文末。
這是什麼¶
一句話:一個 DSH 插件,用於按工作區管理 CLAUDE.local.md,並提供設置面板。
- 它解決的是「項目約定文字」的存放和加載,不解決智能體調度、工具編排這類問題。
- 注入環節不歸它管:真正負責把規則塞進會話上下文的是 DSH 內置的
dsh-agent-instructions。 - 插件壞了、被跳過了,磁盤上那份
CLAUDE.local.md照樣會被原生加載器讀到。這一點是它設計上的兜底。
核心能力¶
1、按工作區隔離。規則文件就在項目目錄下,A 項目的約定不會出現在 B 項目裏,不需要額外的命名空間配置。
2、本地私有,不進版本庫。配合 .git/info/exclude,同事在 Cursor、Codex、CC 裏看不到任何痕跡。
3、保存時的 git 泄露防護。保存前自動補 exclude;如果文件已經被索引追蹤,直接拒絕保存並提示執行 git rm --cached。子模塊和 worktree 都做了兼容。
4、加載狀態可判定。面板會用會話裏真實的 digest 比對,告訴你「當前會話讀到的規則是不是磁盤上這份」,而不是憑猜測。
5、備份不落在項目裏。覆蓋寫入前,舊內容存到 $DSH_HOME/backups/workspace-rules/。這一點是刻意的:備份如果落在項目目錄裏,會讓 exclude 的精確匹配失效。
6、零運行時依賴、零構建。源碼即產物,裝完就能用。
安裝與啓用¶
從 npm 安裝,web 換成你想裝進去的 profile 名:
dsh plugin --profile web add dsh-workspace-rules
裝完重啓 DSH 進程,設置頁會多出「工作區規則」一節。
從 GitHub 源碼安裝,拿到的是最新提交,可能比 npm 上的新:
dsh plugin --profile web add github:winson-young/dsh-workspace-rules
開發期想掛本地源碼:
dsh plugin --profile web add link:D:/Projects/dsh-workspace-rules
注意用 link: 而不是 file:。file: 是把源碼複製進 profile,之後改代碼完全不生效,而且沒有任何提示。
安裝範圍上有一條硬約束:每個 profile 是獨立環境,桌面版由 Electron 獨佔管理,CLI 裝不進 desktop profile。桌面版想要這個功能,得從 npm 或 profile 機制走,不直接用上述命令。
典型用法¶
啓用後,四步把約定落盤:
- 打開「設置 → 工作區規則」。
- 選擇或填寫工作區目錄。
- 寫下這個項目的約定。
- 保存。
約定本身是普通 Markdown:
# 編碼約定
- 不要寫無意義的強制類型轉換
- 所有 SQL 收斂進 model 層,不要在 service / handler 裏拼 SQL
- 改動前先確認現有實現,別重複造輪子
保存後的行爲需要說清楚:新會話在第一步就讀到最新內容;當前會話不一定。工作區層會熱重載,但已經進入上下文的舊指令不會被撤回。所以不必爲了生效去重開對話,但也別指望舊規則在當前窗口立刻失效。
面板底部的診斷區會告訴你幾件事:當前加載狀態、取自哪個會話、指令文件的加載鏈(只顯示文件存在與否和字節數)、以及 exclude 路徑。排障時從那裏看起。
它怎麼改動你的工作區¶
數據鏈路分三層,搞清楚這層關係,後面讀源碼和排障都會簡單一點。
DSH 內置加載器 dsh-agent-instructions ← 真正負責注入
│ 讀取
▼
<項目>/CLAUDE.local.md ← 數據,純 Markdown
▲
│ 讀寫(HTTP)
本插件 dsh-workspace-rules ← 只管編輯
host: /api/workspace-rules
client: 設置頁面板
host 端(lib/index.js)通過 ctx.connection.fetch.register 註冊 GET/POST /api/workspace-rules,走 Connection 的 /api 通道,因此自動落在 Host/Origin 柵欄和瀏覽器鑑權之內。client 端(lib/client.js)手寫 window.__ModuleLoader__.load bundle,require('react') 之後 createElement 渲染,沒有 JSX、沒有打包器。
寫盤有幾道保護:臨時文件加 rename 做原子寫,遇到 EPERM / EBUSY 會重試,重試還不行就降級爲直寫並在響應裏標記;併發用 baseMtimeMs 做樂觀鎖,衝突返回 409,並區分 appeared / changed / removed 三種情況。
限制與注意¶
這些都是 README 裏明確寫出來的限制,裝之前值得先過一遍。
- 沒有會話級隔離。同一工作區的所有會話共享同一份規則文件。
- 只管理
CLAUDE.local.md這一個文件名。同目錄下如果還有AGENTS.md、CLAUDE.md、AGENTS.local.md,面板只提示它們存在,不改動、不預覽。 exclude檢查是保存時刻的一次性動作。之後別人改了忽略規則,插件不知道。手動補一條也簡單:
echo "CLAUDE.local.md" >> .git/info/exclude
用 .git/info/exclude 而不是 .gitignore,理由前面說過了:前者不進版本庫。
- 當前目錄不在 git 倉庫裏時,插件降級爲警告,不會阻斷保存。如果你只是想留一份本地草稿,這沒問題。
- 原生加載器按目錄鏈加載,從項目根掃到當前會話目錄的每一層,越具體的文件優先級越高。這是 DSH 的行爲,不是本插件的行爲,別在這裏找它的配置項。
兼容性方面,README 給出的範圍是 DSH >= 0.2.1-rc.5 < 0.3.0-0,Node >= 20,已在 0.2.1-rc.5 和 0.2.2-rc.1 上實測,運行時依賴爲零。這個範圍寫在 package.json 的 engines.dsh 裏,DSH 在加載 bundle 前做准入判斷:範圍外的版本(0.3.0 起)會直接跳過本插件並在診斷裏寫明原因,而不是裝進去之後靜默出錯。箇中緣由 README 也解釋得直接:不聲明 engines.dsh 又不寫 peerDependencies,等於對所有版本宣稱兼容,哪天宿主改了 API,壞的是運行時,用戶只看到面板打不開。
是否適合你¶
適合的人:同時跑多個項目、每個項目都有自己的編碼約定、不想再每個會話開頭重複一遍的人;以及想把約定留在本地、不希望它進了公共倉庫的人。
不適合的人:需要按會話隔離規則、或者需要團隊共享一份統一約定的場景。前者它不做,後者應該走 CLAUDE.md 這類進版本庫的文件,插件本身不負責管理那些文件。
安裝前照例做兩件事。一是確認來源,從 GitHub 源碼安裝時最好先看看 lib/index.js 和 lib/client.js,插件是以當前 DSH 進程權限運行的。二是知道許可證是 MIT。倉庫目前星標爲 0,規模不大,源碼本身不長,值得花十分鐘讀完。
結尾¶
它的價值很集中:把「項目私有約定」這個詞落到一個具體文件上,換來的是新會話自動載入、本地不進版本庫、壞掉也不影響原生注入。功能面不大,README 裏的限制也說得足夠坦白。想試用的話,目錄頁和源碼都在下面。
- 目錄頁:https://www.skillhub.cn/plugins/winson-young/dsh-workspace-rules
- GitHub:https://github.com/winson-young/dsh-workspace-rules