用 ! 前綴在 command 中執行 shell 指令,自動注入 diff、檔案內容、測試結果,讓 Claude 拿到真實資訊而非模糊描述
大多數人的 slash command 長這樣:
審查當前改動的程式碼品質,給出具體建議。
這能用,但有個根本限制:Claude 得自己去推斷「當前改動是什麼」。如果你的 command 能主動把上下文注入進去,效果會完全不同。
本文要講的是如何讓 command 變成「有眼睛」的指令——觸發時自動讀取檔案內容、git 狀態、專案資訊,把這些塞進提示詞裡,讓 Claude 不用猜。
!`命令` 注入 Shell 輸出自訂 slash command 支援在提示詞裡內嵌 shell 命令,語法是用反引號包住命令並在前面加驚嘆號:!`命令`。觸發時命令先執行,輸出取代占位符,Claude 收到的是最終拼好的提示詞。
以下是當前的 git diff:
!`git diff HEAD`
請審查這些改動,重點關注邏輯錯誤與安全問題。
多行命令請用圍欄程式碼區塊形式,開頭寫
```!:```! node --version git status --short ```
觸發 /review 時,Claude 收到的實際內容是:
以下是當前的 git diff:
diff --git a/app/models/user.rb b/app/models/user.rb
index 3a2f1c8..9b4e2d1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -12,6 +12,9 @@ class User < ApplicationRecord
...
請審查這些改動,重點關注邏輯錯誤與安全問題。
不需要你手動複製貼上,command 觸發的瞬間,diff 已經在裡面了。
---
allowed-tools: Bash(cat:*)
---
以下是當前檔案的完整內容:
!`cat $ARGUMENTS`
找出這個檔案裡所有潛在的效能問題,給出具體的行號與改法。
用法:/perf app/models/order.rb
$ARGUMENTS 接收檔案路徑,cat 把內容讀進來注入。Claude 拿到的是真實程式碼,不是「請看當前檔案」這種模糊描述。
allowed-tools在 frontmatter 裡預先授權了cat命令,觸發時不會跳權限確認。沒有這一行的話,每次執行都要手動點「允許」。下面的範例都需要用同樣方式宣告。
當前分支與改動狀態:
!`git status --short`
!`git log --oneline -10`
根據以上資訊,產生一份簡潔的 PR 描述,包含改動內容與動機。
這個 /pr command 不需要你描述「我改了什麼」,它自己會去讀。
專案技術棧:
!`cat .claude/context/stack.md`
當前資料庫結構(關鍵資料表):
!`head -100 db/schema.rb`
根據以上背景,為 $ARGUMENTS 撰寫符合專案規範的 migration 腳本。
把專案背景資訊提前寫進 .claude/context/ 目錄,command 觸發時按需讀取,不用每次重複描述專案狀況。
最近一次測試執行結果:
!`bundle exec rspec --format progress 2>&1 | tail -30`
以上是失敗的測試。分析根本原因,給出修復方案,不要改測試本身。
/fix-tests 觸發時直接跑測試、拿結果,Claude 看到的是真實的錯誤訊息。
把上面這些組合起來,/review 可以做到相當精準:
---
description: 審查當前改動,自動注入 diff 與相關背景
allowed-tools: Bash(git diff:*), Bash(cat:*)
---
## 本次改動
!`git diff HEAD`
## 改動涉及的檔案清單
!`git diff HEAD --name-only`
## 專案編碼規範摘要
!`cat .claude/context/coding-standards.md 2>/dev/null || echo "(無規範檔案)"`
---
請審查以上改動:
1. 邏輯正確性:有沒有邊界條件沒處理、邏輯錯誤
2. 安全問題:SQL injection、權限驗證、敏感資訊外洩
3. 規範符合度:是否符合專案編碼規範
4. 可讀性:命名、註解、結構
每個問題請給出檔名、行號、具體建議。沒有問題就說沒有,不要硬湊。
這個 command 不需要你做任何準備——觸發時,diff、檔案清單、編碼規範全部自動注入。
Shell 命令可能會失敗(檔案不存在、命令不在 PATH 裡等)。用 || 提供預設值,避免 command 因此中斷:
!`git diff HEAD 2>/dev/null || echo "(無 git 改動或不在 git 倉庫中)"`
!`cat .env.example 2>/dev/null || echo "(無 .env.example 檔案)"`
!`which rspec > /dev/null 2>&1 && bundle exec rspec --dry-run 2>&1 | head -20 || echo "(未偵測到 RSpec)"`
命令失敗時,Claude 收到的是說明文字而不是空白,它可以據此調整回答策略。
! 命令的輸出全部會進入 context window。幾個常見的坑:
不要 cat 整個大檔案
# 危險:可能注入幾萬行
!`cat db/schema.rb`
# 更好:只取需要的部分
!`grep -A 5 "create_table \"orders\"" db/schema.rb`
日誌檔案加上行數限制
!`tail -50 log/development.log`
測試輸出截斷
!`bundle exec rspec 2>&1 | tail -40`
context 越精準,Claude 的回答越好。把整個程式碼庫塞進去不會讓它更聰明,只會讓它迷失在雜訊裡。
常見的問題:專案背景資訊應該放 CLAUDE.md 還是用 ! 動態注入?
判斷標準:
| 資訊類型 | 放哪裡 |
|---|---|
| 每次都需要的專案背景(技術棧、目錄結構、規範) | CLAUDE.md |
| 隨時間變化的狀態(當前 diff、測試結果、檔案內容) | ! 動態注入 |
| 特定任務才需要的背景(某個模組的設計說明) | !cat 按需讀取 |
CLAUDE.md 是長期的背景知識,! 是任務當下的即時快照。兩者互補,不要重複。
.claude/commands/ 目錄.claude/
├── commands/
│ ├── review.md # 審查:注入 diff + 規範
│ ├── test.md # 寫測試:注入目標檔案
│ ├── fix-tests.md # 修測試:注入失敗結果
│ ├── pr.md # PR 描述:注入 git log + status
│ ├── explain.md # 解釋程式碼:注入檔案內容
│ └── migrate.md # 寫 migration:注入 schema 片段
├── context/
│ ├── stack.md # 技術棧說明
│ └── coding-standards.md # 編碼規範
└── settings.json
context/ 目錄存放靜態背景檔案,供各個 command 按需讀取。這套結構可以直接 commit 進 git,讓團隊共享。
靜態 command:
請審查當前程式碼改動。
Claude 得先問「改動在哪裡」,或是自己去找,結果不穩定。
上下文感知 command:
這是 diff(附實際內容)、這是相關規範(附實際內容),請審查。
Claude 直接開始分析,回答精準,不廢話,不需要來回確認。
差別不在於 Claude 的能力,而在於你給了它多少真實資訊。