Hub Google AI CLI 教學

第 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 或指令裡寫死絕對路徑或分隔符號。敏感設定用 settingssensitive: 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 色彩名稱(如 coralteal),可以混用;文件建議至少填 background.primarytext.primarytext.secondary 與幾個 accent/status 色維持可讀性,其餘巢狀屬性技術上都是選填。

官方文件也支援從檔案載入 theme,作法是把 settings.jsontheme 值設成檔案路徑,但為了降低風險,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),這是目前最常見的根因,症狀常被誤判成套件壞掉。
改了 .tomlgemini-extension.json,沒有生效單純 commands/*.toml 編輯用 /commands reload 即可;牽涉 extension 安裝狀態的改動要重開整個 CLI session(見 10.3)。
自訂主題怎麼填都載入失敗,甚至整份 settings.json 讀不進去社群 曾回報較新的主題欄位被驗證器判定成不合法 key;先只填 background.primarytext.primarytext.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 promptCustom 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.jsonmcpServersexcludeToolssettings、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 的過渡期間,這些細節變動又更快。

動手試試

  1. 在測試專案設計一個 .gemini/commands/review.toml,只使用文字 prompt 與 {{args}}
  2. 加上一個 @{docs/review-checklist.md},確認注入的檔案不含 secret 或私人資料。
  3. 把同一個 command 改成 namespaced 版本,例如 .gemini/commands/team/review.toml,用 /commands list 確認名稱。
  4. /theme 試一個內建 light theme 與 dark theme,觀察 diff、錯誤與連結顏色是否清楚。
  5. 找一個你信任的 extension repo,只讀它的 gemini-extension.json、commands 與 MCP server 程式碼,不要急著安裝。
  6. 在一個還沒信任的資料夾裡裝一個 extension 或寫一個 command,觀察畫面上的反應,再用 /permissions 信任它,比較前後差異。
  7. 跑一次 gemini extensions new 建立範本 extension,用 gemini extensions link 連結成開發模式,改一行 gemini-extension.json 確認怎麼看得出改動生效。