Prefixer:先限縮範圍,miss 再擴大,並記錄差異
一個搜尋整個儲存庫的代理,會為每個不相關的命中支付 Token 成本。而一份命名空間地圖,短短十幾行描述每個東西在哪裡,就能讓它從第一次呼叫就從正確的區域開始搜尋。這個技能負責建立這份地圖、維護它,並強制代理遵循「先限縮範圍」的搜尋原則。它的唯一工作就是:更便宜、更好的程式碼庫搜尋。
大小門檻。 當檔案少於大約一百個,或儲存庫的結構一目了然時,跳過地圖,直接以目錄名稱限縮搜尋範圍;超過這個門檻,地圖才值得其成本。
運作方式
- 定位一次。 檢查是否存在
.prefixer/namespaces.md。如果不存在且任務需要超過兩次搜尋,則為你正在處理的區段繪製地圖(§1 至 §3),然後在該地圖內進行搜尋。如果地圖已存在,讀取它並從限縮範圍開始。 - 限縮搜尋,miss 再擴大。 第一次搜尋從最狹窄的合理命名空間開始,而非儲存庫根目錄(§4 至 §6)。
- 記錄它。 在任務結束時,在
.prefixer/metrics.jsonl中寫入一行紀錄,並在存在基線時記錄差異(§7 至 §8)。這是必要輸出,非選擇性。
何時值得使用
- 首次接觸大型或不熟悉的儲存庫。 讀取十二行地圖取代掃描整個目錄樹;第一次搜尋就已經是限縮範圍的。
- Monorepo。 每個套件一個命名空間:搜尋不會再將兄弟套件的雜訊拉進上下文中。
- 多個程式碼庫。 組織層級的地圖選定程式碼庫,儲存庫的地圖則選定其內的命名空間。兩個層級,絕不進行聯集掃描。
- 重複探索。 「X 在哪裡」、重構、不熟悉的功能區域;首次嘗試即命中的機率,正是地圖價值的所在。
- 你會再次造訪的儲存庫。 地圖會跨工作階段持續存在;未來的每個代理都可以直接從已定位的狀態開始,無需重新掃描。
A. 命名空間地圖
1. 繪製區段地圖,而非全域地圖
規則: 當儲存庫沒有 .prefixer/namespaces.md 且工作需要超過兩次搜尋時,繪製 此任務所觸及的儲存庫部分 的地圖:三到五個命名空間;而非整個目錄樹。一份能限縮今日搜尋範圍的部分地圖,勝過一份花了一小時才完成的全域地圖。當一次真實搜尋落在地圖之外時才擴展它,切勿預先擴展。執行 bash scripts/init_namespaces.sh(位於此技能的目錄中)以取得骨架,然後修正其猜測:說明每個命名空間是 用來做什麼的。
原因: 採用設計上是漸進式的。地圖是一個工作檔案,而非普查;當一個搜尋需要某個條目時,該條目才值得存在。
草稿不是地圖。 任何前綴或 contains 欄位仍為 TODO 的條目都是未完成的;在依賴該地圖前,請完成它或刪除它。骨架腳本會報告剩餘的草稿條目數量。
優先使用現有文件。 如果 AGENTS.md、CLAUDE.md 或 README 已經描述了佈局,則從它們那裡填充 contains 欄位(骨架腳本會這麼做),並將 namespaces.md 視為 搜尋範圍 的權威來源;不要維護兩份逐漸偏離的目錄。
工作階段開始時閱讀,勝過靠記憶。 每個工作階段閱讀一次地圖,是最容易被跳過的步驟。如果 harness 能在工作階段開始時注入檔案,讓它輸出 namespaces.md:那麼每次搜尋都將從限縮範圍開始,無需任何紀律要求。提供的封裝:scripts/install_hook.sh 為你寫入鉤子(scripts/session_start.sh 會輸出地圖加上健康狀態報告);手動程式碼片段位於 integrations/。配方與原理說明:references/namespaces.md。
2. 保持小巧並以 Glob 為基礎
規則: 5 到 12 個命名空間。每個條目都是一個簡短的前綴、一個路徑 glob,以及一行為了消除歧義而寫的目的說明。如果兩個條目同樣頻繁地匹配同一個搜尋,請合併它們。如果一個條目從未實際限縮過任何搜尋,請刪除它。
格式:
# namespaces
repo: acme-payments
| prefix | scope | contains |
|--------|--------------------|-----------------------------|
| api: | src/api/** | REST 處理程序、路由 |
| auth: | src/auth/** | 登入、工作階段、令牌 |
| sso: | src/auth/sso/** | SSO 子團隊區域 |
| test: | tests/**, **/*.test.* | 測試套件、fixtures |
repo: 標頭是此程式碼庫的唯一限定詞(來自 git remote);當一個組織運行多個程式碼庫時,它使每個前綴都是全域唯一的:acme-payments:auth: 永遠不會與 checkout-service:auth: 衝突。
條目可以嵌套:一個縮排的條目屬於其上方的命名空間,用於模組內的子團隊或服務內的套件。標籤會組合,例如 auth:sso:,因此兩個父級下的相同名稱會保持不同(orders:checkout: vs payments:checkout:)。
名稱是衍生而非宣告的。 前綴和 contains 文字來自程式碼庫自身的詞彙:目錄名稱、條目所限縮範圍內檔案的 dominant 術語,絕不來自你發明的分類。衍生名稱會隨著儲存庫的演進而追蹤;發明的名稱則會逐漸偏離。骨架腳本會為你做到這點:修正它搞錯的部分,保留它做對的部分。
格式細節與其他儲存庫形狀的地圖:references/namespaces.md。
3. 更新,絕不重新產生
規則: 當地圖過期時,編輯過期的條目。不要重寫整個檔案。全面重新產生會丟失使其有用的累積細節。清除死掉的條目,添加缺失的條目,保持其他部分完全相同;重寫地圖也會破壞載入它的每個工作階段的提示快取前綴。整理完後,執行 bash scripts/check_namespaces.sh:一個 glob 解析後找不到任何檔案的條目,就是地圖正在對每個未來工作階段撒的謊。當地圖感覺過期或日誌已增長時,執行 bash scripts/suggest_map.sh:它會讀取 metrics.jsonl 和目錄樹,並提議真實搜尋已經要求過的條目。
多個程式碼庫: 同樣的技巧可以堆疊。組織層級的地圖選定程式碼庫(pay: services/payments/**),儲存庫地圖則選定其內的命名空間。每個地圖的 repo: 標頭使前綴在程式碼庫之間保持唯一;四十個服務都可以有一個 auth/,因為一個限定的標籤 payments:auth: 只會指向一個。絕不將每個儲存庫聯集成一個搜尋,這正是組織規模下根目錄 grep 的失敗模式。請參閱 references/namespaces.md。
B. 搜尋範圍限縮
4. 從最狹窄的開始
規則: 每次搜尋都從可能包含答案的最狹窄命名空間開始。grep "expire" src/auth/ 勝過 grep "expire" .。一次 miss 花費一次呼叫;一次廣泛的搜尋則會花費整個命中列表的 Token。
讓腳本承擔規則: bash scripts/search.sh auth "expire" 會將標籤解析為其 glob,在其中搜尋,若 miss 則擴大一個層級,並記錄指標行;§4 至 §8 自動執行。--scope <ns> 印出 glob 列表,--paths <ns> 印出解析後的檔案,--list 印出所有標籤;地圖成為一個其他指令可以查詢的索引。
注意:「以防萬一」而搜尋儲存庫根目錄、沒有路徑限制的 glob 模式如 **/*、以及在命名空間 glob 就能解決時卻使用 find。
5. Miss 時擴大一個層級
規則: 在命名空間內 miss 了?擴大一個層級:命名空間,然後其父區域,然後整個儲存庫,然後是 vendored 和生成的目錄。不要從一次 miss 就直接跳到整個儲存庫掃描,也不要搜尋 node_modules、vendor、dist、build 或鎖定檔案,除非任務本身就是關於它們。
6. 使用命名空間標籤回答
規則: 以 prefix:path:line(例如 auth:src/auth/session.ts:42)的格式報告發現。下一次搜尋,無論是你的還是未來工作階段的,都將從已限縮的範圍開始。當一個檔案無法匹配任何命名空間時,這表明地圖有缺口;請提及它,而不是發明一個標籤。
C. 追蹤
7. 在做任何宣稱之前先建立基線
規則: 在建議搜尋變更之前,先計算當前行為:每個任務的搜尋次數、每個任務的工具呼叫次數、首次嘗試命中率。沒有基線就沒有差異,只有一個故事。透過執行一個固定的探測來記錄基線:在儲存庫根目錄下執行 bash scripts/measure_search.sh --baseline orientation -- grep -rn "TODO" .,或者計算地圖存在前最初幾個任務的指標。請參閱 references/metrics.md 了解要記錄什麼以及如何評分。
8. 記錄每個任務
規則: 在每個任務結束時,將一行 JSON 附加到 .prefixer/metrics.jsonl:
{"ts":"2026-10-26T14:03:11Z","task":"find-session-expiry","searches":3,"tool_calls":4,"first_try_hit":true,"est_tokens":1400}
est_tokens 是字元數/4,一個估計值;即使你無法精確測量,也請保留此欄位。在工作階段結束時,報告與基線的差異。scripts/measure_search.sh 封裝一次搜尋,並在儲存庫根目錄記錄每次搜尋的行(hits、scope;--baseline 標記該次為探測,--hit|--miss 記錄 first_try_hit)。手動撰寫的任務行會添加 tool_calls 和 first_try_hit。一個 schema,可選欄位;references/metrics.md 列出了它們。scripts/report_metrics.sh 讀取日誌:命中率、每個範圍的花費、限縮範圍與基線的差異。
Git 處理。 提交 namespaces.md:它是每個未來工作階段都能受益的共享前綴。metrics.jsonl 是本機遙測資料;骨架腳本會為它寫入一個 .prefixer/.gitignore。僅當團隊想要趨勢資料時才提交它。
紅旗警訊
這些想法意味著該停下來了。你正在合理化。
| 想法 | 現實 |
|---|---|
| 「一次全面的 grep 不會有事的」 | 全面的 grep 正是這個技能存在要消除的成本。限縮它。 |
| 「我知道它大概在哪裡」 | 在命名空間中驗證。錯誤的猜測比一次限縮範圍的檢查花費更多。 |
| 「先繪製整個儲存庫的地圖」 | 繪製你目前所在區段的地圖。當搜尋需要時,其餘部分才會獲得條目。 |
| 「對這個任務來說,地圖太小題大作了」 | 到第三次搜尋時,部分地圖仍然會回本。 |
| 「跳過基線,直接應用就好」 | 沒有基線就代表沒有證據。先測量。 |
| 「我會重新產生整個地圖,這樣比較快」 | 重新產生會丟失精心整理的細節並破壞快取。編輯過期的條目。 |
| 「一個骨架地圖就算數了」 | 充滿 TODO 條目的地圖是草稿,不是地圖。完成或刪除它們。 |
| 「搜尋 vendor 目錄以求徹底」 | 生成的程式碼是 Token 的墳墓。只在它是任務主題時才搜尋它。 |
何時不該使用前綴
- 單一檔案腳本和一次性儲存庫:沒有什麼可以限縮範圍的。
- 只有一個明顯檔案的任務:直接打開它,不要為它建立基礎設施。
- 當路徑本身就說明了:在
src/auth/內部的搜尋不需要auth:這樣的儀式。 - Vendored 或生成的程式碼,除非它是任務的主題。
- 大約少於 100 個檔案且結構明顯的儲存庫:以目錄名稱限縮範圍,跳過地圖。
參考資料
references/namespaces.md:地圖格式、儲存庫形狀、多儲存庫堆疊references/metrics.md:指標定義、JSONL 結構、差異報告scripts/init_namespaces.sh:為當前儲存庫建立命名空間地圖骨架scripts/check_namespaces.sh:檢查地圖:死掉的範圍、重複標籤、草稿scripts/search.sh:作為指令的限縮範圍搜尋:解析、搜尋、擴大、記錄scripts/suggest_map.sh:讀取日誌 + 目錄樹,提議地圖編輯scripts/measure_search.sh:執行一次搜尋並一步記錄scripts/report_metrics.sh:讀回日誌:命中率、範圍花費、差異scripts/session_start.sh:為工作階段開始鉤子輸出地圖(+ 健康狀態)scripts/install_hook.sh:為 devin/claude 設定 SessionStart 鉤子scripts/doctor.sh:健康檢查儲存庫的設定:地圖、檢查、鉤子、安裝記錄scripts/eval_scope.sh:測量限縮範圍搜尋與根目錄搜尋的 Token 差異scripts/eval_repo.sh:檢查並評估地圖中每個命名空間,平均節省百分比scripts/visualize.py:將地圖渲染在檔案樹上,並將測量到的節省量呈現為 HTML 報告SPEC.md:namespaces.md 格式合約,供讀取或寫入地圖的工具使用integrations/:工作階段開始鉤子程式碼片段 + 給消費儲存庫的 PR lint 工作流程
為管線和提示作者保留的相鄰操作指南(不屬於此技能的核心工作):references/index-prefixes.md、references/prefix-library.md、references/cache-ordering.md