Skip to content
Back to skills

Cbm Usage

ASecurity

用 codebase-memory-mcp(程式碼知識圖譜)查結構時的判準與陷阱。 當要查「某個符號定義在哪」「某檔被誰 import」「某函式呼叫了誰」「有多少死碼/孤兒」 「這次改動影響哪些地方」,或任何要引用 cbm 查詢數字下結論時觸發。 也涵蓋「為什麼被 cbm 查詢閘擋下來」「這個數字能不能用」的判讀。

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 24, 2026
developmentpythongobashangularspringapi

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned September 24, 2026

npx -y skills add abs1294/fulin-claude-plugins --skill cbm-usage --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cbm Usage?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cbm Usage
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/abs1294-cbm-usage/badge)](https://www.skillsdirectory.com/skills/abs1294-cbm-usage)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.