Back to skills
SKILL.md
Cbm Usage
ASecurity用 codebase-memory-mcp(程式碼知識圖譜)查結構時的判準與陷阱。 當要查「某個符號定義在哪」「某檔被誰 import」「某函式呼叫了誰」「有多少死碼/孤兒」 「這次改動影響哪些地方」,或任何要引用 cbm 查詢數字下結論時觸發。 也涵蓋「為什麼被 cbm 查詢閘擋下來」「這個數字能不能用」的判讀。
- 2 stars
- 0 votes
- 0 copies
- 1 view
- Added September 24, 2026
Works with
Security analysis
100/100npx -y skills add abs1294/fulin-claude-plugins --skill cbm-usage --agent claude-codeAre you the author of Cbm Usage?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/abs1294-cbm-usage)---
name: cbm-usage
description: >
用 codebase-memory-mcp(程式碼知識圖譜)查結構時的判準與陷阱。
當要查「某個符號定義在哪」「某檔被誰 import」「某函式呼叫了誰」「有多少死碼/孤兒」
「這次改動影響哪些地方」,或任何要引用 cbm 查詢數字下結論時觸發。
也涵蓋「為什麼被 cbm 查詢閘擋下來」「這個數字能不能用」的判讀。
---
# 用 cbm 查程式碼結構:什麼可信、什麼會騙人
> **一句話**:它的數字很容易看起來很成功——精確、可複製、有來源,**唯獨是錯的**。
> 引用任何查詢數字前,先讀完本檔的「三條鐵則」。
本 plugin 附帶一支 PreToolUse hook,會在查詢當下攔截已知會誤導的用法。
**被擋下來不是工具壞了**,是那個查法會給你一個看起來對的錯數字。
---
## 三條鐵則(不分專案,永遠適用)
### 鐵則一:任何計數都要同時查總數
```
查到 X 筆 → 同時查總數 → X 接近總數 = 判準沒鑑別力
```
`260 / 260` 與 `10 / 480` 意義完全不同,**單看 260 分不出來**。
這是唯一不會過期的自保動作——文件會錯、索引會重建,對照組不會。
**互補驗證**是最快的檢查法:同一個 pattern 的 `EXISTS` 與 `NOT EXISTS`
兩邊相加應該等於總數。不等於就是寫法還沒對。
⚠ **但互補驗證有盲區**:它證明得了「寫法對」,證明不了「索引是新的」。
舊索引的 657+611 與新索引的 570+698 都等於 1,268,兩組各自都自洽。
### 鐵則二:「查不到人用」不等於「沒人用」
靜態分析看不到這幾類引用,它們全都會被算成孤兒或死碼:
| 看不到的 | 例子 |
|---|---|
| 動態載入 | `() => import()`、變數路徑的 `require()` |
| barrel 捷徑 | `index.js` 轉出,引用記在 barrel 上 |
| 框架樣板綁定 | template 的 `@click`、`:prop` 傳函式 |
| 框架反射入口 | MediatR `Handle`、EF Core `Configure`、Spring `@Component`、Angular `ngOnInit` |
| DI 容器 | 建構子由容器呼叫 |
| 介面宣告 | 呼叫記在實作上,兩者 qualified_name 不同 |
| 測試檔與設定檔 | 本來就沒人 import |
**實測參考**:某專案正確寫法算出 131 個孤兒模組,逐一 grep 後**真孤兒只有 7 個**;
後端死碼查詢回 68.7%,抽樣後**沒有一筆能安心刪**。
⚠ 而且「無人引用」**也不等於可以刪**——未啟用功能、保留給日後的完整實作、
公開 API 性質的 export 都會落在清單裡,該不該刪往往是**產品決策不是技術判斷**。
### 鐵則三:引用結論要附盲區與反向樣本
不是貼查詢結果就算。引用「零缺口」「有 N 個死碼」這類結論時,要同時給:
1. **這個查詢看不到什麼**(一句話)
2. **一個反向樣本**——拿已知存在的問題餵給它,確認它抓得到
只貼「掃描通過/查詢回 0 筆」=未完成。
---
## 索引會過期,而它不會告訴你
**cbm 的查詢結果不帶新鮮度標記。** 對著兩天前的索引查,回傳的節點數、行號、
關係邊都長得跟新鮮的一模一樣,不報錯、不警告。
實測案例:四個 project 有兩個索引比 HEAD 舊 40 小時以上,而該段期間正好有
一輪死碼清理。對那兩個 project 查「某元件還在不在」會拿到**已經刪掉的檔案**。
```bash
python "${CLAUDE_PLUGIN_ROOT}/scripts/cbm_index_freshness.py" --fix
```
判準=索引時間 < 該 repo HEAD 的 commit 時間。重建成本極低(110K 行約 9 秒),
**過期就重跑,不必猶豫**。
---
## 寫 Cypher 的兩個硬性限制
### EXISTS 子查詢:綁定變數必須在 pattern 開頭
這是引擎限制,不是專案問題。從匿名節點起頭時子查詢解析不出來,
結果恆為「全部命中」或「零命中」——**而且不報錯**。
```cypher
❌ MATCH (f:Function) WHERE NOT EXISTS { ()-[:CALLS]->(f) } RETURN count(f)
→ 恆等於 Function 總數(100% 命中)
✅ MATCH (f:Function) WHERE NOT EXISTS { (f)<-[:CALLS]-() } RETURN count(f)
→ 正確
```
**改法:把外層綁定的變數挪到 pattern 第一個位置,箭頭方向反過來。**
```
()-[:R]->(x) → (x)<-[:R]-()
(:Label)-[:R]->(x) → (x)<-[:R]-(:Label)
```
### 方言限制(只支援 openCypher 讀取子集)
| 寫法 | 改用 |
|---|---|
| `WHERE NOT (a)-[:X]->(b)` | `WHERE NOT EXISTS { (a)-[:X]->(b) }` |
| 多段 `WITH` 管線 | 拆成多次查詢,或在外部聚合 |
| `WHERE` 裡字串串接 `+` | `STARTS WITH`/`ENDS WITH`/`CONTAINS` |
| 任何寫入(`CREATE`/`SET`/`MERGE`/`DELETE`) | 不支援,唯讀 |
---
## 節點數 ≠ 實體數
同一個「東西」常常對應多個節點或多條邊:
```
某 api 檔的 importer 23 條邊 → 14 個不同檔案
前端 Route 270 顆節點 → 150 個不同端點
```
兩種成因不同:
- **邊查詢**:`MATCH (a)-[:R]->(b)` 同一個 a 有多條邊就多列
→ 要算「幾個 a」加 `count(DISTINCT a.file_path)`
- **Route 之類**:同一實體真的建多顆節點(一顆帶 query string 樣板、一顆乾淨版)
→ 加 `count(DISTINCT r.name)`
**先問自己:我要的是「節點數」還是「不重複的實體數」?** 兩個都報出來最安全。
---
## 什麼時候用它(它真的比 grep 強的場景)
判準一句話:**問「東西在哪」用它,問「東西有沒有被用」要小心。**
| 場景 | 怎麼問 | 為什麼它強 |
|---|---|---|
| 找同名/相近命名的東西 | `search_graph(query="...")` | 它認得 qualified_name,grep 只認字串 |
| 某個符號定義在哪 | `search_graph` 的 `name_pattern` 參數(吃 regex) | 回檔案+行號+label,可直接跳 |
| 某檔被誰 import | `MATCH (f:File)-[:IMPORTS]->(m:Module) WHERE m.file_path='...'` | 正向查詢可靠 |
| 某函式呼叫了誰(出向) | `trace_path(direction="outbound")` | 出向邊準確——**除非有執行期分派** |
| 誰呼叫了某函式(入向) | `trace_path(direction="inbound")` | 入向可靠 |
| 看檔案結構概況 | `get_file_outline`/`get_architecture` | 比逐檔讀快 |
| 確認 schema | `get_graph_schema(diagnostics="full")` | **每次寫 Cypher 前的第一步** |
**共同點:這些都是「查事實」,不是「下判斷」。**
⚠ 先查 schema,不要猜屬性名——例如欄位叫 `file_path` 不是 `file`。
---
## 什麼時候不要用它
| 不可用 | 症狀 | 改用 |
|---|---|---|
| **死碼偵測**(有框架反射的專案) | 回報比例高得不合理 | 編譯器/linter 的「未使用」警告 |
| **執行期分派的呼叫鏈** | `trace_path` 出向回 `callees_total: 0` | 型別名人工對應,grep 即可 |
| **attribute routing 的後端路由** | `Route` 節點建不出來,跨站配不起來 | 兩邊各自抽取後自寫比對器 |
| **孤兒模組的精確數字** | 正確寫法仍高估數倍 | 拉清單逐筆 grep,或引擎外自寫 resolver |
---
## 設定檔(選填)
專案根放 `cbm-guard.config.json`,可以讓提醒訊息帶上**你的專案事實**
(框架反射入口清單、實測數字、替代方案路徑、已標註的孤兒檔)。
**不放也能用**——五類通用判準照常生效。範本見 plugin 根目錄的
`cbm-guard.config.example.json`。
兩個欄位會**額外啟用**判準:
- `reflectionEntryPoints` → 啟用死碼查詢的 deny
- `runtimeDispatch` → 啟用 `trace_path` 出向的提醒
沒有框架反射問題的專案不填即可,不會被無關的提醒干擾。
---
## 最後三句
1. **「工具壞了」是最舒服的歸因,也最該被懷疑。** 多數情況真因是自己的查詢寫法。
2. **任何計數都要同時查總數當對照組。** 命中率接近 100% 或 0% 就是判準壞了。
3. **文件本身會錯,而讀到錯文件的人會停止查證。** 沒讀到文件的人反而會自己查——
所以照本檔做之後,**仍要自己跑對照組**。
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.