第 4 篇 高手 · 第 10 章
Extensions、custom commands、themes
這章把 Gemini CLI 的三種擴充層次拆清楚:custom commands 解決重複 prompt,extensions 打包工具與工作流,themes 改善終端機閱讀與團隊一致性。
版本與產品狀態都要以當下為準
Gemini CLI 文件與指令表面變動很快;本章觀念適合建立判斷,但實作前請先跑 /help、/commands list、/extensions list、/theme 或查看目前官方文件。Google 在 2026-05-19 公告 Antigravity CLI 過渡,並說明 2026-06-18 後免費、Google AI Pro/Ultra 與 Gemini Code Assist for individuals 路線的 Gemini CLI 請求會停止服務;企業、Google Cloud、Gemini Code Assist Standard/Enterprise 與 paid API key 路線依公告仍有不同安排。這裡採保守說法:先確認你的帳號與版本,再決定要投資 Gemini CLI 還是 Antigravity CLI。
10.1 先分清三件事
custom command、extension package、theme 都叫「擴充」,但解決的問題不同。不要一開始就包 extension;如果只是把一段常用 prompt 儲存下來,custom command 就夠了。
| 項目 | 用途 | 何時使用 |
|---|---|---|
| Custom command | 把常用 prompt 變成 /review、/git:commit 這類 slash command。 | 你只需要重用文字指令、注入檔案內容,或在使用者確認後跑一小段 shell。 |
| Extension package | 把 MCP servers、commands、GEMINI.md、themes、hooks、skills、subagents 與 policy 打包。 | 你要分享給團隊、跨專案安裝,或需要一組工具與上下文一起版本化。 |
| Theme | 調整 Gemini CLI 的顏色、差異檢視、狀態色與閱讀體驗。 | 你要固定個人偏好、支援無障礙需求,或讓團隊終端機輸出更一致。 |
10.2 Custom commands:把流程變成 slash command
Custom commands 是最低成本的自動化。Gemini CLI 會讀取全域 ~/.gemini/commands/ 與專案 .gemini/commands/ 裡的 TOML 檔;專案指令可以進版本控制,適合讓整個 repo 共用。
# .gemini/commands/git/commit.toml
description = "根據 staged diff 產生 Conventional Commit 訊息。"
prompt = """
請根據下方 staged diff 產生一個 Conventional Commit 訊息。
要求:
- 標題不超過 72 個字元
- 必要時加上簡短 body
- 不要自動提交
```diff
!{git diff --staged}
```
"""
這個檔案會變成 /git:commit。!{...} 會在送出 prompt 前執行 shell command,Gemini CLI 會要求確認;{{args}} 會帶入使用者在 slash command 後輸入的參數;@{...} 可把檔案或目錄內容注入 prompt。
# .gemini/commands/review.toml
description = "依照專案 review checklist 檢查指定檔案或主題。"
prompt = """
你是一位謹慎的 code reviewer。
Review 目標:{{args}}
請依照這份 checklist 檢查,先列高風險問題,再列測試缺口:
@{docs/review-checklist.md}
"""
/commands list
/commands reload
/review src/auth/session.ts
/git:commit
參數怎麼帶進去:固定的展開順序
前面兩個例子分別示範了 !{...} 跟 {{args}},但沒講一個實作時很關鍵的細節:Gemini CLI 處理一個 custom command 時,三種語法的展開順序是固定的——永遠先展開 @{...} 檔案內容,接著執行 !{...} shell 指令並注入結果,最後才做 {{args}} 的參數替換。知道這個順序,就能放心把三種語法疊在同一個 prompt 裡,不用擔心誰先誰後會影響結果;甚至可以在 prompt 開頭寫死「你只能規劃、不能動手改程式碼」這類純文字約束,做出一個只出策略、不碰檔案的指令變體,不需要額外語法。
prompt 裡完全沒寫 {{args}} 也不代表使用者輸入會被吃掉——Gemini CLI 仍然會把它自動接到整段 prompt 最尾端,前面空兩行分隔;只有想精準控制參數該插進哪個段落,才需要顯式寫出 {{args}}。!{command} 執行前一定會先跳出確認,若執行失敗,stderr 訊息與結束碼也會一併注入 prompt,模型看得到「這一步失敗了、失敗訊息是什麼」,不會誤以為一切正常。@{path} 指到資料夾時會遞迴嵌入整個目錄,遵守 .gitignore 與 .geminiignore;圖片、PDF、音訊、影片這類檔案能被正確編碼送進模型,遇到不支援的二進位檔則優雅跳過,不會讓整個指令中斷報錯。
指令怎麼命名、撞名怎麼處理
指令的名字不是你在 TOML 裡填的欄位,而是由檔案的相對路徑決定:commands/test.toml 會變成 /test;放進子資料夾,子資料夾名稱就變成命名空間、用冒號分隔——這正是前面 commands/git/commit.toml 變成 /git:commit 的原因。使用者層級(~/.gemini/commands/)與專案層級(.gemini/commands/)剛好定義了同名指令時,專案層級永遠優先,讓進版本控制的團隊指令可以蓋掉個人習慣的全域指令。
Extension 帶來的指令撞名時,處理邏輯不一樣:不是冒號命名空間,而是用 extension 名稱當前綴、以點號分隔,例如 /acme-review.review。兩種撞名解法分屬不同機制,別混著記;不確定目前實際會呼叫哪個版本,/commands list 或 /help 直接看現況,比用猜的準。
MCP server 自己也能長出 slash command
如果第 9 章接的某個 MCP server 本身就定義了 MCP Prompts,完全不用再幫它包一層 TOML——Gemini CLI 會自動把該 server 提供的 prompt 名稱與描述轉成同名的 slash command,呼叫時可以用具名參數,也可以直接依序給位置參數。這代表如果你同時是某個 MCP server 的開發者,在 server 端多定義幾個 prompt,比要求每個使用者各自手寫 TOML 省事得多——裝了這個 MCP server 的人都自動拿到對應指令。前提跟 10.4 會細講的一樣:資料夾要先被信任,MCP server 才連得上,這些 prompt 也才看得到。
/mycommand --topic="auth flow"
/mycommand "auth flow" security
10.3 Extension packages:把一組能力打包
Extension 是可安裝、可分享的 package。它的核心是根目錄的 gemini-extension.json,旁邊可以放 MCP server、commands、GEMINI.md、skills、hooks、subagents 與 policy;theme 則由 manifest 的 themes array 定義。官方文件把它定位成把 prompts、MCP servers、commands、themes 與其他代理能力包成可安裝格式。
acme-review-extension/
gemini-extension.json
GEMINI.md
commands/
review.toml
git/
commit.toml
hooks/
hooks.json
skills/
release-notes/
SKILL.md
agents/
reviewer.md
policies/
review-policy.toml
dist/
server.js
hooks、skills、agents 各自完整的檔案格式,第 11 章會細講;這裡先知道它們在 extension 裡放哪個資料夾即可。contextFileName 沒有特別指定時,只要目錄下有 GEMINI.md,預設就會被當成 context 檔載入。
{
"name": "acme-review",
"version": "1.0.0",
"description": "Acme 團隊的 review commands、MCP tools 與 CLI theme。",
"contextFileName": "GEMINI.md",
"mcpServers": {
"acme-review": {
"command": "node",
"args": ["${extensionPath}${/}dist${/}server.js"],
"cwd": "${extensionPath}",
"timeout": 30000,
"includeTools": ["search_guidelines", "read_policy"]
}
},
"settings": [
{
"name": "Acme API token",
"description": "用來讀取 Acme 內部 guideline 的唯讀 token。",
"envVar": "ACME_GUIDE_TOKEN",
"sensitive": true
}
],
"themes": [
{
"name": "acme-focus",
"type": "custom",
"background": {
"primary": "#101418"
},
"text": {
"primary": "#eef2f6",
"secondary": "#a8b3bf",
"link": "#7ab7ff"
},
"status": {
"success": "#8fd17f",
"warning": "#f0c674",
"error": "#ff8a80"
},
"border": {
"default": "#344150"
}
}
]
}
Extension 裡的 mcpServers 與一般 settings.json 的 MCP server 很像,但要用 ${extensionPath}(extension 自身所在資料夾的絕對路徑)、${workspacePath}(使用者目前專案的絕對路徑)與 ${/}(依平台自動代入 / 或 \)這三個變數讓 package 可移植,不要在 manifest 或指令裡寫死絕對路徑或分隔符號。敏感設定用 settings 與 sensitive: true——這個值會走系統 keychain 存放,而且沒有在 settings 裡明確宣告的環境變數,預設完全不會被傳進 extension 或它啟動的 MCP server 行程,就算你的 shell 環境裡本來就有那個變數也一樣,這是刻意的最小權限設計。不要要求使用者把 token 寫進 README 或 manifest。
manifest 完整欄位一覽
上面的範例只示範最常用的幾個欄位,gemini-extension.json 實際支援的欄位更完整,整理如下——用不到的可以先跳過,但知道有這些選項存在,之後要處理版本遷移或自訂 plan 產物路徑時才不用整個重查文件。
| 欄位 | 必填 | 說明 |
|---|---|---|
name | 必填 | 只能用小寫字母、數字與 dash,不能有底線或空白;同名衝突時會被當作指令前綴 |
version | 選填 | semver 版本號 |
description | 選填 | 一行描述,安裝清單與 Gallery 頁面會顯示 |
mcpServers | 選填 | 物件,定義這個 extension 要啟動哪些 MCP server |
contextFileName | 選填 | 沒指定、但目錄下有 GEMINI.md 時,預設就會載入它 |
excludeTools | 選填 | 陣列,封鎖特定工具或工具的特定呼叫模式 |
settings | 選填 | 陣列,宣告使用者可設定、可綁定環境變數的欄位 |
themes | 選填 | 陣列,內建自訂主題定義 |
migratedTo | 選填 | 指向新 repo 的網址,供 CLI 提示使用者自動遷移 |
plan | 選填 | 規劃產物(plan mode 輸出)的預設存放資料夾 |
欄位可能隨版本增減,寫 manifest 前建議對照當下的官方 extension reference 頁面,尤其是 name 的命名規則——用了大寫、底線或空白,安裝時很可能直接被拒絕。
白名單、黑名單,以及 extension 不能自己宣告的信任
manifest 裡的 mcpServers 除了 includeTools 白名單,也能反過來用 excludeTools 定義黑名單,而且支援到「指令層級」的細粒度:"excludeTools": ["run_shell_command(rm -rf)"] 只封鎖那一個特定的 shell 呼叫,run_shell_command 其餘合法用法仍然可以用;只寫 "run_shell_command"(不帶括號參數)才是整個工具都被排除。另一個容易被忽略的限制是:extension 裡的 mcpServers 明確不支援 trust 欄位——extension 不能自己幫自己的 MCP server 宣告信任層級,這道關卡最終仍在使用者與資料夾信任機制手上(見 10.4)。
CLI 安裝與管理指令
安裝來源可以是 GitHub repo 網址,也可以是本機資料夾路徑,指令語法完全一樣,差別只在 <source> 這個參數傳什麼;本機開發用 new 建骨架、link 建 symlink,改程式碼立即反映,不需要每次重新 install。
gemini extensions install https://github.com/example/acme-review-extension --ref v1.0.0
gemini extensions install ./local-extension-folder --auto-update
gemini extensions list
gemini extensions uninstall acme-review
gemini extensions enable acme-review --scope workspace
gemini extensions disable acme-review --scope workspace
gemini extensions update acme-review
gemini extensions update --all
gemini extensions config acme-review
gemini extensions new my-team-extension mcp-server
gemini extensions link .
/extensions
/extensions reload
常用旗標:--ref 鎖定特定 tag 或 commit、--auto-update 讓之後版本更新自動套用、--pre-release 允許安裝預發布版、--consent 跳過確認提示(CI/CD 用,但等於放棄了安裝前人工核閱來源與其宣告的 MCP servers 這一步,不要對不信任的來源用)、--skip-settings 跳過安裝時的設定流程。
安裝第三方 extension 不會靜默完成:CLI 會先列出風險警語、即將啟動哪些 MCP server、用什麼指令啟動、會不會寫入或附加你的 GEMINI.md,問過 [Y/n] 才真的安裝。掃一眼「用什麼指令啟動」那一行最快——看到直接 npx -y 跑遠端套件,代表你連帶信任了那個 npm 套件的整條供應鏈。
改了 extension 卻好像沒生效?
官方文件提到,extension 的安裝、更新、停用、啟用(包括它捆綁的 slash commands、MCP servers、GEMINI.md 內容變化)都需要重新啟動整個 Gemini CLI session 才會確實生效。/extensions reload 適合「只是改了 extension 內部檔案,想重新讀取一次」;牽涉安裝狀態改變的操作(新裝、移除、啟用、停用),還是先整個重開 session 再確認比較保險,兩者容易混在一起,改完後誤以為沒作用。
10.4 Trusted Folders 怎麼卡住 extensions 與 commands
第 1 章提過,Gemini CLI 第一次在陌生資料夾執行都會先問要不要信任;第 7 章有更完整的信任判斷準則。這裡只講一件事:資料夾不被信任時,本章教的三種擴充,待遇並不一樣。
在還沒被信任的資料夾裡,Gemini CLI 不會安裝、更新或移除 extension;不會嘗試連線任何 MCP server——包括 extension 帶進來的那些,stdio 型 MCP server 只有資料夾被信任才會顯示 Connected,否則就是 Disconnected;也完全不會載入任何 .toml 自訂指令。三者一次全部卡住,介面上通常不會明講「因為資料夾沒被信任」,很容易誤判成擴充本身有問題。Theme 不在這個限制範圍內:純視覺設定不牽涉工具執行或指令載入,不會被信任機制擋下。
「裝了但沒作用」,九成先查這個
延伸套件照著文件裝好,指令、MCP server 卻好像完全不存在——先別急著懷疑 manifest 寫錯或版本不合。社群 不少「延伸套件不生效」的回報,追根究底都是資料夾信任這關沒過,不是套件本身壞掉。花幾秒跑一次 /permissions,或直接打開 ~/.gemini/trustedFolders.json 確認這個資料夾在不在信任清單裡,通常比重裝、重查 manifest 快得多。
10.5 Themes:先解決可讀性,再談風格
Theme 可以用 /theme 選,也可以寫進 settings.json。如果你只是個人偏好,使用內建 theme 就好;如果團隊需要固定 terminal 截圖、diff 顏色、錯誤警示色,才考慮自訂 theme 或把 theme 放進 extension。
{
"ui": {
"theme": "GitHub Light"
}
}
{
"ui": {
"customThemes": {
"Acme Focus": {
"name": "Acme Focus",
"type": "custom",
"background": {
"primary": "#101418",
"diff": {
"added": "#17351f",
"removed": "#3b1717"
}
},
"text": {
"primary": "#eef2f6",
"secondary": "#a8b3bf",
"link": "#7ab7ff",
"accent": "#f0c674"
},
"status": {
"success": "#8fd17f",
"warning": "#f0c674",
"error": "#ff8a80"
},
"border": {
"default": "#344150",
"focused": "#7ab7ff"
}
}
}
}
}
色彩值支援 hex 色碼(如 #1a362a)或標準 CSS 色彩名稱(如 coral、teal),可以混用;文件建議至少填 background.primary、text.primary、text.secondary 與幾個 accent/status 色維持可讀性,其餘巢狀屬性技術上都是選填。
官方文件也支援從檔案載入 theme,作法是把 settings.json 的 theme 值設成檔案路徑,但為了降低風險,theme 檔案的位置有安全限制:Gemini CLI 只會載入使用者家目錄之內的主題檔案,路徑一旦指到家目錄之外——例如某個團隊共用資料夾,或 /tmp——就會顯示警告,該主題不會被載入。若 theme 由 extension 提供,安裝並啟用後會出現在 /theme 清單,通常會帶上 extension 名稱方便辨識來源。
內建主題一覽
不想自訂,先看內建的夠不夠用。以官方文件目前列出的清單,大致分深色系與淺色系兩組,實際名稱與數量以 /theme 選單或 gemini --help 現場輸出為準,隨版本增減是常有的事:
| 分類 | 主題 |
|---|---|
| 深色系 | ANSI、Atom One、Ayu、Default、Dracula、GitHub、Holiday、Shades Of Purple、Solarized Dark、Tokyo Night |
| 淺色系 | ANSI Light、Ayu Light、Default Light、GitHub Light、Google Code、Solarized Light、Xcode |
settings.json 已經寫死 theme,選單怎麼選都沒用
如果 settings.json 裡已經有 theme 欄位——不管值是主題名稱還是外部檔案路徑——/theme 互動選單即使選了新主題,也不會真的持久化生效。要先手動把 settings.json 裡的 theme 欄位整段拿掉,才能讓 /theme 對話框正常運作、記住你的選擇。這是新手很容易卡住的地方:明明照著選單操作了,畫面卻好像什麼都沒變。
10.6 常見錯誤與排除
多數延伸套件相關的問題都跟信任狀態、連線環境或設定檔語法有關,很少是套件本身設計錯誤。先看下面的速查表,比較需要多幾個步驟排除的情境,表格後面另外展開。
| 狀況 | 先查什麼 |
|---|---|
| extension 裝了、指令或 MCP server 卻完全沒反應 | 先查資料夾信任狀態(見 10.4),這是目前最常見的根因,症狀常被誤判成套件壞掉。 |
改了 .toml 或 gemini-extension.json,沒有生效 | 單純 commands/*.toml 編輯用 /commands reload 即可;牽涉 extension 安裝狀態的改動要重開整個 CLI session(見 10.3)。 |
自訂主題怎麼填都載入失敗,甚至整份 settings.json 讀不進去 | 社群 曾回報較新的主題欄位被驗證器判定成不合法 key;先只填 background.primary、text.primary、text.secondary 測試能否載入,能載入再逐步加欄位排查。 |
| MCP server 連線出現 SSL/TLS 憑證相關錯誤 | 常見於公司網路的中間人攔截,見下方展開說明。 |
| MCP server 啟動失敗,看起來像連不上 | 也可能只是連接埠被別的程序佔用;停掉佔用該埠的程序,或在 MCP server 設定裡改埠,跟信任沒過的 Disconnected 狀態要分開排查。 |
| 開機沒看到任何錯誤,但延伸套件裡的 MCP server 好像沒作用 | 背景連線錯誤預設是靜默的,通常只會印一行「MCP issues detected. Run /mcp list for status.」;主動跑一次 /mcp list 才看得到完整診斷。 |
| macOS 上本機 MCP server 從外部工作目錄啟動不了 | 社群 回報過沙盒環境會擋這種狀況;改用 ${extensionPath}/${workspacePath} 這類變數而不是寫死外部路徑,通常能繞開。 |
SSL/TLS 憑證錯誤:公司網路最常見的卡點
在公司網路連 MCP server 時,很常見的失敗模式是連線報 SSL/TLS 憑證相關錯誤。根因通常是公司防火牆做了 SSL/TLS 中間人攔截,用自己的根憑證重新簽發流量,但 Node.js 預設不認得這張公司自己簽的憑證。建議先試比較省事的做法,不行再上比較明確的做法:
# 先試:讓 Node.js 改用作業系統原生的憑證庫
NODE_USE_SYSTEM_CA=1 gemini
# 仍失敗,再指到公司自訂根憑證檔案
NODE_EXTRA_CA_CERTS=/path/to/corp-ca.pem gemini
這類錯誤畫面上常常語焉不詳,第一直覺容易懷疑是 MCP server 設定寫錯,但先排除憑證問題通常比逐欄位重查設定檔快。
10.7 什麼時候用哪一種?
| 需求 | 建議 | 理由 |
|---|---|---|
| 我常打一段很長的 review prompt | Custom command | 不用建 package,TOML 就能版本化與分享。 |
| 我要讓模型讀內部 API 或資料庫 | MCP server;若要分享再包 extension | 新工具與資料來源要有明確 schema、權限與輸入驗證。 |
| 我要把 commands、MCP、GEMINI.md 一起給全團隊 | Extension package | 安裝、停用、更新與設定流程比較一致。 |
| 我只想讓終端機變亮色或高對比 | Theme | 不應為視覺偏好引入工具權限。 |
| 我要發布給外部使用者 | Extension package 加上版本、release notes 與安全審查 | 外部使用者需要可追溯來源與明確更新路徑。 |
| 延伸套件裝了卻好像沒作用 | 先查資料夾是否已信任 | 這是 10.4 提到的頭號根因,通常不是套件本身壞掉。 |
10.8 安全審查清單
擴充會把新的 prompt、shell、MCP server 或設定帶進你的本機環境。審查時不要只看功能,也要看它能讀什麼、寫什麼、傳去哪裡。
- 資料夾信任:先用
/permissions或檢查~/.gemini/trustedFolders.json確認要操作的資料夾已被信任——見 10.4,這是最容易漏掉、卻最常見的第一關。 - 來源:只安裝你信任的 repo、tag 或 commit;團隊使用時固定
--ref,避免無意追到不相容變更。 - Manifest:檢查
gemini-extension.json的mcpServers、excludeTools、settings、themes 與 policy。 - Shell:搜尋 custom command 裡的
!{...},確認每個 shell command 都必要、可預期且不會刪檔或上傳資料。 - 檔案注入:檢查
@{...}是否會讀太大的目錄、secret、private docs 或不該放進 prompt 的資料。 - MCP 權限:使用最小
includeTools,避免 broad shell、任意 filesystem、任意 URL fetch 或寫入型工具。 - Secrets:API key 應透過 extension settings 或環境變數提供,並標成 sensitive;不要把 secret 寫在 command、GEMINI.md 或 theme file。
- 更新:自動更新要搭配信任邊界;關鍵工作流建議先在測試 workspace 跑
gemini extensions update。 - 衝突:custom command 可能和使用者或專案指令同名;用
/help和/commands list確認實際會呼叫哪一個。
10.9 小型實作範例:團隊 review 包
先從最小可用版本開始:一個 project custom command。確定團隊每天都用,再把它升級成 extension。
# .gemini/commands/team/review.toml
description = "用團隊準則 review 指定變更。"
prompt = """
請用繁體中文 review:{{args}}
審查順序:
1. 先找 correctness、security、data loss、auth bypass。
2. 再找測試缺口。
3. 最後只列必要的維護性建議。
團隊準則:
@{docs/team-review-guide.md}
"""
升級成 extension 時,把同一份 TOML 放到 extension 的 commands/team/review.toml,再加上 GEMINI.md 描述團隊風格。如果後來需要查內部規範,才加入唯讀 MCP tool;安裝完記得重開一次 CLI session(見 10.3),用 /commands list 確認看到的是 /team:review 這個 extension 版本,不是還在讀舊的 project command。
本章小結
先用 custom command 固定重複 prompt;當能力需要跨專案安裝、更新與分享時,再包成 extension;theme 則專注在可讀性與一致性。每次導入擴充都要做來源、shell、MCP、secret、更新與衝突審查,資料夾信任是最容易漏掉卻最常見的第一關;改動涉及安裝狀態時記得重開 session 才會生效。用你目前版本的 /help 和官方文件確認指令是否一致,Gemini CLI 與 Antigravity CLI 的過渡期間,這些細節變動又更快。
動手試試
- 在測試專案設計一個
.gemini/commands/review.toml,只使用文字 prompt 與{{args}}。 - 加上一個
@{docs/review-checklist.md},確認注入的檔案不含 secret 或私人資料。 - 把同一個 command 改成 namespaced 版本,例如
.gemini/commands/team/review.toml,用/commands list確認名稱。 - 用
/theme試一個內建 light theme 與 dark theme,觀察 diff、錯誤與連結顏色是否清楚。 - 找一個你信任的 extension repo,只讀它的
gemini-extension.json、commands 與 MCP server 程式碼,不要急著安裝。 - 在一個還沒信任的資料夾裡裝一個 extension 或寫一個 command,觀察畫面上的反應,再用
/permissions信任它,比較前後差異。 - 跑一次
gemini extensions new建立範本 extension,用gemini extensions link連結成開發模式,改一行gemini-extension.json確認怎麼看得出改動生效。