用 gws CLI + Claude Code 清理 13 年沒整理的 Gmail:完整操作教學
我的 Gmail 用了 13 年,從來沒整理過。
89,275 封信。foodpanda 促銷、LinkedIn 通知、Uber One 廣告、各種驗證碼。每次打開信箱看到那個數字就想關掉。我是一個很不喜歡刪東西的人,總覺得「搞不好以後會用到」,結果就是 13 年什麼都沒刪。
這週末花了一個下午,用 gws CLI + Claude Code 清掉 54,124 封,信箱直接少了 40%。
這篇是完整的操作教學,照著做就行。
前置準備
開始之前需要三樣東西:
1. 安裝 gws CLI
gws 是 Google Workspace 的官方 CLI 工具。裝完之後 Gmail、Calendar、Drive 全部變成 terminal 指令。最重要的是——所有輸出都是 JSON,AI Agent 可以直接解析。
brew install googleworkspace-cli沒用 Homebrew 的話,還有其他安裝方式:
# npm
npm install -g @googleworkspace/cli
# 或從 source 編譯
cargo install --git https://github.com/googleworkspace/cli --locked完整安裝選項可以看 GitHub repo。

2. OAuth 認證
gws 使用 OAuth 2.0 認證。第一次需要建立 GCP 專案和 OAuth client:
# 一次性設定:建立 Cloud 專案、啟用 API、登入
gws auth setupgws auth setup 需要先安裝 gcloud CLI。如果你沒裝 gcloud,會看到以下錯誤

也可以手動到 Google Cloud Console 建立 OAuth client,然後把 client_secret.json 放到 ~/.config/gws/client_secret.json,繼續下一步
設定完成後登入:
# 登入(選擇要授權的服務範圍)
gws auth login這時會需要選擇 scope,記得要選擇 gmail,其他看需求

瀏覽器會跳出 Google 授權頁面,點授權就好。

授權完會選擇存取範圍,選擇完按繼續看到「You may now close this window」就可以關掉視窗回到 Terminal。
重要提醒: 跑完 gws auth setup 和 gws auth login 之後,瀏覽器跳出 Google 授權頁面,但你看到的不是授權按鈕,而是這個畫面:
已封鎖存取權:
「claude code」未完成 Google 驗證程序
這個應用程式目前處於測試階段,只有獲得開發人員核准的測試人員可以存取。
如果您認為自己應該擁有存取權,請與開發人員聯繫。
發生錯誤 403:access_deniedgws auth setup 建立的 OAuth 同意畫面預設是「測試」狀態(未發布)。Google 規定:
- 測試模式的 OAuth app,只有被加入「測試使用者」清單的 Google 帳號才能授權
- 你的 Gmail 帳號雖然是 GCP 專案的擁有者,但不會自動被加入測試使用者清單
所以你需要手動把自己的 Gmail 帳號加進去。
網路上很多教學,這裡就不贅述~
認證完成後,token 會用 AES-256-GCM 加密存在本機 keyring,之後不用重新登入。
確認認證狀態:
gws auth status會看到類似這樣的輸出:
{
"auth_method": "oauth2",
"token_valid": true,
"user": "你的email@gmail.com",
"enabled_api_count": 35,
"scope_count": 50
}完整的認證教學可以參考 gws README 的 Authentication 段落。
用完之後建議到 Google 帳戶安全性設定 檢查授權範圍,不需要的話可以撤銷。
3. Claude Code
需要 Claude Code(Max 或 Pro 方案)。
npm install -g @anthropic-ai/claude-code3.1. 安裝 gws 的 Claude Code Skills(推薦)
這是很多人不知道的部分。gws 內建了 95 個 Agent Skills,專門給 Claude Code 用。裝了之後 Claude Code 就「知道」怎麼呼叫 gws 的正確語法。
# 一次安裝所有 gws skills
npx skills add https://github.com/googleworkspace/cli
# 或只裝你需要的
npx skills add https://github.com/googleworkspace/cli/tree/main/skills/gws-gmail
npx skills add https://github.com/googleworkspace/cli/tree/main/skills/gws-drive不裝 skill 也能用 gws,但 Claude Code 容易猜錯語法。我第一次操作的時候就踩了這個坑——AI 把指令寫成 gws gmail users.messages.list,跑不過。正確的語法是空格分隔 gws gmail users messages list。裝了 skill 就不會有這個問題。
三樣都備齊,可以開始了。

Step 1:看信箱現況
打開 Claude Code,第一步是搞清楚自己的信箱到底有多少信。
我的 prompt:
「幫我看這個 Gmail 信箱有多少信」
Claude Code 的回應:
它先跑了 gws gmail users getProfile:
gws gmail users getProfile --params '{"userId": "me"}'回傳:
{
"emailAddress": "xxx@gmail.com",
"messagesTotal": 89276,
"threadsTotal": 85689
}然後它主動跑 gws gmail +triage 來看最近的收件匣概覽:
gws gmail +triage這個 helper 指令會列出最近的未讀信件摘要(寄件人、主旨、日期),像這樣:
date from subject
──────────────── ───────────────────────────── ──────────────────────────
Sat, 04 Apr 2026 "Uber 電子明細" <noreply@uber> [Personal],您的訂單
Sat, 4 Apr 2026 foodpanda <contact@info...> 【企客】,你已經錯失好多了?
Sat, 04 Apr 2026 "台新銀行" <TSB@mhurcv...> 連假免出門🏠Uber Eats 23折優惠89,275 封。真的有點扯。
為了方便了解他做了什麼,我把 claude code 實際跑的指令寫出來,但實際上你最後會看到的是他總結給你的結果~
Step 2:讀取特定信件
信箱現況看完了,我想先處理一封研討會的入選通知。
我的 prompt:
「幫我看這封:xxxxxx」
Claude Code 的操作:
它先搜尋符合的信件:
gws gmail users messages list \
--params '{"userId": "me", "q": "subject:研討會 投稿入選", "maxResults": 5}'回傳 message ID 清單後,他用 +read 讀取信件內容:
gws gmail +read --id 19d4da41657c3eb1然後 Claude Code 主動幫我整理重點:
這是某技術研討會的投稿入選通知,重點整理:
緊急:截止日是明天!
需要回覆:
1. 確認出席
2. 提供宣傳照片(至少 500x500 像素 JPG)
3. 確認宣傳資料
你的議程資訊:
- 講題:XXX
- 日期:待定它不只是把信件內容貼出來,還會自動判斷「哪些是你需要做的事」,這是 AI Agent 跟傳統 CLI 最大的差別。
Step 3:草擬回覆信
信讀完了,要回覆確認出席。
我的 prompt:
「好,草擬回覆信,圖片附件在 ~/Downloads/xxxx.jpg」
Claude Code 的操作:
它先確認圖片存在和尺寸:
sips -g pixelWidth -g pixelHeight ~/Downloads/xxxx.jpg
# pixelWidth: 1994
# pixelHeight: 2448確認 1994x2448 符合 500x500 以上要求後,用 +reply-all(因為信裡要求用全部回覆)建立草稿:
gws gmail +reply-all \
--message-id 19d4da41657c3eb1 \
--body '<p>您好,</p>
<p>感謝您的提醒。以下確認事項:</p>
<ol>
<li><strong>出席確認</strong>:兩日都可以到場。</li>
<li><strong>宣傳照片</strong>:請參閱附件。</li>
<li><strong>宣傳資料</strong>:目前資訊無需修改。</li>
</ol>
<p>Best regards,<br>Jason Yang</p>' \
--html \
-a xxxx.jpg \
--draft注意: -a 附件的檔案路徑必須在當前工作目錄內,所以它先把照片 cp 到工作目錄再附加。
回傳:
{
"id": "r3413428723371210479",
"message": {
"id": "19d597af94f38088",
"labelIds": ["DRAFT"],
"threadId": "19d4da41657c3eb1"
}
}草稿建立成功!存在 Gmail 草稿匣裡,可以先到 Gmail 確認內容再手動送出。
用 --draft flag 是個好習慣——讓 AI 先建草稿,你確認後再寄。不要讓 AI 直接送出信件。
Step 4:搜尋舊發票
接下來處理另一件事:我要找一張幾個月前買線上課程的發票。只記得大概是去年七八月,花了xxx台幣(美金顯示)。
我的 prompt:
「我想要找某個課程之前購買的發票,信件有點多,大概是在去年七八月左右,價格是xxx台幣,但應該是顯示美金」
Claude Code 的操作:
它先用關鍵字搜尋:
gws gmail users messages list \
--params '{"userId": "me", "q": "課程名稱 after:2025/06/01 before:2025/10/01", "maxResults": 10}'第一次搜太窄沒結果,它自動放寬條件,拿掉「發票」只留課程名稱:
gws gmail users messages list \
--params '{"userId": "me", "q": "課程名稱 after:2025/06/01 before:2025/10/01", "maxResults": 10}'找到幾封候選信件後,再加上付款相關關鍵字篩選:
gws gmail users messages list \
--params '{"userId": "me", "q": "課程名稱 (invoice OR receipt OR 付款 OR payment) after:2025/06/01 before:2025/10/01", "maxResults": 10}'找到 3 封候選,它用 +read 逐一讀取,最終找到付款平台的收據:
gws gmail +read --id 19xxxxxxxxxxReceipt from ○○○ Limited Receipt #XXXX-XXXX
Amount paid: $XXX.00
Date paid: Aug X, 2025
Payment method: Visa ****XXXX
Summary:
- 某課程 早鳥方案 × 1
- Amount paid : $XXX.00金額跟我記憶中的吻合,確認就是這張。
整個搜尋過程大概 10 秒。如果用 Gmail 網頁版手動搜,光翻頁就要翻很久。
Step 5:轉寄購買證明
找到發票後,我需要轉寄給課程方處理要事。
我的 prompt:
「幫我把這封發票轉寄到 support@某某.site,說我要申請xxx」
Claude Code 的操作:
它用 +forward 轉寄原始收據,並在上方加上說明:
gws gmail +forward \
--message-id 19xxxxxxxxxx \
--to support@某某.site \
--body '<p>您好,</p>
<p>以下附上當初的購買收據作為證明。</p>
<p>Best regards,<br>Jason</p>' \
--html \
--draft同樣用 --draft 先存草稿。原始收據會自動帶在轉寄信件裡,不需要手動貼。
Step 6:批次清理信箱
前面的操作讓我確認 gws + Claude Code 的組合很順手。接下來進入正題——清理 89,275 封信。

6.1 盤點各類別數量
我的 prompt:
「我想整理信件,你幫我開一個 agent team 來研究怎麼整理」
Claude Code 派出兩個平行 agent:一個做清理策略規劃,一個分析信箱現狀。
分析 agent 跑了這些查詢來估算各類別數量:
# 促銷信(1年前)
gws gmail users messages list \
--params '{"userId": "me", "q": "category:promotions older_than:1y", "maxResults": 500}' \
--page-all --page-limit 100
# 社群通知(6個月前)
gws gmail users messages list \
--params '{"userId": "me", "q": "category:social older_than:6m", "maxResults": 500}' \
--page-all --page-limit 100
# 驗證碼(1個月前)
gws gmail users messages list \
--params '{"userId": "me", "q": "subject:(驗證碼 OR \"verification code\" OR OTP) older_than:1m", "maxResults": 500}' \
--page-all --page-limit 100--page-all 是個很好用的 flag,自動幫你翻頁,每頁輸出一行 JSON(NDJSON 格式)。不用自己寫 pagination 邏輯。
結果:
| 類別 | 查詢條件 | 數量 |
|---|---|---|
| 促銷郵件(1年前) | category:promotions older_than:1y |
37,300 封 |
| 社群通知(6個月前) | category:social older_than:6m |
6,382 封 |
| 驗證碼(1個月前) | subject:(驗證碼 OR OTP) older_than:1m |
78 封 |
光這三類就佔信箱將近 49%。
6.2 抽樣確認
這一步很重要,不要跳過。
Claude Code 自動抽樣每個類別的前幾封,列出寄件人和主旨讓我確認:
# 取得前 5 封的 message ID
gws gmail users messages list \
--params '{"userId": "me", "q": "category:promotions older_than:1y", "maxResults": 5}'
# 逐封讀取 metadata
gws gmail users messages get \
--params '{"userId": "me", "id": "195fbe4176a1908d", "format": "metadata", "metadataHeaders": ["Subject","From","Date"]}'結果:
Anna Anisin <anna.a@formulatedby.com> | AI & Data Weekly
foodpanda <contact@info.foodpanda.com.tw> | 【企客】,你已經錯失好多了?
Max <no-reply@recommendations.max.com> | 上線倒數計時開始:《最後生還者》第 2 季
"天下學習" <cwlearning@cw.com.tw> | 【週末微學習,限時體驗】
Uber One <uberone@uber.com> | 4 月 Uber One 會員專屬優惠攻略來了!全部都是典型促銷垃圾。社群通知也是:
LinkedIn <notifications-noreply@linkedin.com> | You have 4 new invitations
LinkedIn <messages-noreply@linkedin.com> | Full Stack Engineer: 台灣大哥大...
LinkedIn <jobs-listings@linkedin.com> | Google is hiring a Software Engineer確認安全後,才進入批次清理。
6.3 開 Agent Team 平行清理
我的 prompt:
「三個一起來,開 agent team 處理」
Claude Code 同時派出 3 個背景 agent,每個負責一個類別:
Agent 1 — 促銷信(37,300 封):
# 1. 收集所有 message ID
gws gmail users messages list \
--params '{"userId": "me", "q": "category:promotions older_than:1y", "maxResults": 500}' \
--page-all --page-limit 100 | jq -r '.messages[]?.id'
# 2. 備份 ID 到本地檔案
# 存到 /tmp/gmail-promo-ids.txt
# 3. 批次移到垃圾桶(每次最多 1000 封)
gws gmail users messages batchModify \
--params '{"userId": "me"}' \
--json '{"ids": ["id1","id2",...], "addLabelIds": ["TRASH"], "removeLabelIds": ["INBOX"]}'Agent 2 — 社群通知(6,382 封): 同樣流程,query 換成 category:social older_than:6m
Agent 3 — 驗證碼(78 封): 同樣流程,query 換成驗證碼關鍵字
三個 agent 平行執行,互不干擾。促銷那批最大,分了 38 批處理(每批 1000 封),耗時約 5 分鐘。
安全措施
整個過程有三道安全網:
- 抽樣確認過了 — 每個類別都人工看過樣本
- Message ID 備份 — 所有被刪的 ID 都存在
/tmp/gmail-*-ids.txt - 只用
batchModify移到垃圾桶 — Gmail 垃圾桶有 30 天恢復期,不是永久刪除
用 batchModify + addLabelIds: ["TRASH"] 而不是 messages delete(永久刪除)。這個差別很重要。
Step 7:驗收結果
三個 agent 跑完後,Claude Code 自動回報:
全部完成!三個 agent 的清理結果:
| 類別 | 數量 | 狀態 |
|--------------------|------------|------|
| 促銷郵件(1年前) | 37,300 封 | 完成 |
| 社群通知(6個月前) | 6,382 封 | 完成 |
| 驗證碼(1個月前) | 78 封 | 完成 |
| 合計 | 54,124 封 | 全部移至垃圾桶 |信箱從 89,275 封降到約 45,515 封,清掉了將近一半。
之後又進行了幾輪更深入的清理:
| 輪次 | 類別 | 數量 |
|---|---|---|
| 第二輪 | GitHub 通知、出貨通知、密碼重設 | 1,408 封 |
| 第三輪 | Medium、Freelancer.com、外送促銷 | 1,957 封 |
| 第四輪 | Uber、Substack、Coursera、LinkedIn、雜項 | 6,999 封 |
| 累計 | 54,124 封(60.6%) |
最終信箱剩約 35,000 封。從 89,275 砍到 35,000,六成都是垃圾。
一個下午做完 13 年沒做的事。
延伸用法:AI 郵件助理
清完之後我才發現,gws + Claude Code 不只能清信,根本是一個完整的郵件助理。
搜尋舊信
前面示範過了。跟 Claude Code 說「幫我找某個課程的收據」,gws 搜尋 + AI 解讀,幾秒就翻出來了。
草擬回覆
收到研討會入選通知,我只說「幫我草擬回覆」,它直接 +reply-all --draft 建好草稿,附件都帶好。
轉寄文件
找到發票後,說「轉寄給某某信箱,說我要做什麼」,它用 +forward --draft 處理。原始收據自動帶在轉寄裡。
Uber 收據分析
我還讓它把所有外送平台收據抓出來,解析成 CSV,然後做成互動式 Dashboard。
以前這些事每件都要打開 Gmail、搜尋半天、點進去、複製貼上。現在一句話搞定。
gws CLI 常用指令速查
| 用途 | 指令 |
|---|---|
| 安裝 | brew install googleworkspace-cli |
| 認證設定 | gws auth setup |
| 登入 | gws auth login |
| 認證狀態 | gws auth status |
| 收件匣摘要 | gws gmail +triage |
| 讀信 | gws gmail +read --id <MSG_ID> |
| 搜尋信件 | gws gmail users messages list --params '{"userId":"me","q":"<query>"}' |
| 回覆 | gws gmail +reply --message-id <ID> --body '<text>' --draft |
| 全部回覆 | gws gmail +reply-all --message-id <ID> --body '<text>' --html --draft |
| 轉寄 | gws gmail +forward --message-id <ID> --to <email> --draft |
| 寄信 | gws gmail +send --to <email> --subject '<subject>' --body '<text>' |
| 附件 | -a <file>(檔案需在工作目錄內) |
| 批次操作 | gws gmail users messages batchModify --params '{"userId":"me"}' --json '...' |
| 自動翻頁 | 加 --page-all --page-limit 100 --page-delay 100 |
所有 + 開頭的是 helper 指令(語法比較人性化),其他的走標準 Google API 路徑。
注意事項 + 踩坑紀錄
踩坑 1:gws 指令語法
gws 的指令格式是 gws <service> <resource> [sub-resource] <method>,參數用 --params 傳 JSON。
# 錯誤 ❌ — 用了點號分隔
gws gmail users.messages.list --user-id me
# 錯誤 ❌ — 少了 users 這層
gws gmail messages list --format json
# 正確 ✅
gws gmail users messages list --params '{"userId":"me","maxResults":100}'關鍵差異:
- 需要
users這個 resource 層級(Gmail API 的路徑是/users/{userId}/messages) - 所有參數都用
--params包成 JSON,不是 CLI flags userId固定傳"me"代表當前授權的帳號
Helper 指令(+read、+reply 等)語法比較直覺,不需要記 API 路徑。建議優先用 helper。
踩坑 2:附件路徑限制
-a 附件的檔案路徑必須在當前工作目錄內,不能用絕對路徑或 ~/ 開頭的路徑:
# 錯誤 ❌
gws gmail +reply --message-id xxx -a ~/Downloads/photo.jpg
# Error: resolves to path which is outside the current directory
# 正確 ✅
cp ~/Downloads/photo.jpg ./photo.jpg
gws gmail +reply --message-id xxx -a photo.jpg踩坑 3:OAuth scope 限制
如果你的 OAuth app 在測試模式(未驗證),Google 限制同意畫面可授權的 scope 數量。用 recommended scope preset(85+ scopes)會失敗。解法是指定只需要的服務:
gws auth login -s gmail,drive建議先小量測試
不要一上來就清全部。先拿一個小類別(像驗證碼,通常只有幾十封)試跑一次完整流程:分類 → 抽樣 → 備份 ID → 移到垃圾桶 → 確認。確認整個流程沒問題再放大規模。
Token 成本
- Google API:免費。Gmail API 有每日配額但一般個人用量不會超過
- Claude Code token:這是主要成本。如果你用月費制一定夠
Claude Code 權限控制
跑批次任務的時候,建議在 Claude Code 的設定裡把 gws 相關指令設為允許,避免每次執行都要手動確認。但 gws gmail messages delete(永久刪除)不要放進自動允許清單。
用 trash(垃圾桶)就好,不要用 delete(永久刪除)。
Comments ()