iT邦幫忙

0

🧾 用 Cursor Agent + Git 自動產出週報:我怎麼一路把 weekly.sh 打磨到能真的用

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20251118/20155103ny9YvKHuQS.png

🧠 為什麼我會想寫一支 weekly.sh?

一開始,我只想做一件看起來很單純的事:

「我可不可以讓 Cursor 每週自動幫我看 Git 專案,產一份 Markdown 週報,順便掃一下有沒有可疑程式碼?」

腦中想像的畫面是這樣的:

  • 每週固定時間自動執行
  • 把最近 7 天的 commit 抓出來
  • AI 幫我整理:
    • 每筆 commit 在做什麼
    • 哪些檔案被新增 / 修改 / 刪除
    • 有沒有硬寫 API key、SQL injection、奇怪的權限操作
  • 自動發送給同事

公司的專案放在Gitlab上面,無法使用Github Action。

🚫 第一個現實打臉:Cursor 本體不會自己跑

我原本以為 Cursor 可以像 cron job 一樣,自己定期觸發 Agent。
實際上:

  • Cursor 沒有排程器
  • Agent 不能自行啟動
  • 也不具備背景服務模式

因此真正需要的是:

由「外部腳本」去呼叫 Cursor Agent,而不是讓 Cursor 自己跑。

🐧 在 Windows 上,我最後老實使用 WSL

Cursor 提供的 CLI 安裝方式全部偏向 macOS/Linux:

curl https://cursor.sh/install -sSfL | bash

PowerShell 不吃這種指令。

最後的做法:

  1. 開 WSL(Ubuntu)
  2. 在裡面安裝 Cursor CLI
  3. 設定 API Key
  4. weekly.sh 全部在 WSL 裡執行

這讓整個流程穩定許多。

📁 /mnt 的大小寫與路徑大坑

Windows 的磁碟會掛在 /mnt 之下,例如:

  • D:\/mnt/d

Linux 大小寫敏感,一個字母錯了就整個路徑找不到。
最後靠 ls 一層一層往上確認才找到正確位置。

🧵 第一版 weekly.sh:只看 Git Log

最初版本做的事很單純:

  • git log 抓最近 7 天的 commit
  • 把 log 原文丟給 Cursor,請它整理成週報

缺點也很明顯:

  • 只有 commit 訊息,看不到程式碼
  • 沒有安全分析
  • 無法協助 code review

因此開始做能力提升。

🔍 加入 git diff:從「知道做了什麼」到「看到怎麼做」

我一開始直接把 diff 全部丟給 AI,但立刻遇到:

  • token 爆炸
  • 執行時間拉長
  • 有時完全跑不完

促使我開始思考:
哪些內容真的要給 AI?哪些該在 shell 自己先整理?

✂️ LV2 + LV3:我最後採用的多層壓縮策略

LV2:判斷 diff 大小

透過 git show --numstat 計算每個 commit 的變更行數:

  • 小 diff:保留新增/刪除行摘要
  • 大 diff:只給統計資訊與敏感片段

LV3:敏感關鍵字篩選

用 grep 自動標記可能危險的地方,例如:

  • password
  • token
  • bearer
  • SQL 語句
  • 連線字串

這能有效找出:

  • 硬寫 API key
  • SQL injection 風險
  • 權限控制問題

🧩 最終的 COMMITS_DETAIL 結構

每個 commit 會被整理成:

===== COMMIT START =====
Commit: <hash>
Author: <作者>
Date: <時間>
Message: <訊息>
TotalChangedLines: <總變更行數>

[FILES]
<檔案清單>

[LARGE_DIFF_SUMMARY]
<大 diff 的統計資訊>

[ADDED_LINES]
<新增行摘要>

[DELETED_LINES]
<刪除行摘要>

[SENSITIVE_CANDIDATES]
<敏感關鍵字命中的片段>

===== COMMIT END =====

這些內容再一次丟給 Cursor 進行解讀。

🧪 我調整思考框架的幾個重要觀念

1. Agent 不會自己拿 repo 的東西

CLI 的 Agent 看不到你的 repo,
它只能吃你餵進 prompt 的文字。

2. 把輸出位置寫在 prompt 裡是一種方便但需小心的方法

方便,但可能會受之後的工具更新影響。
目前我接受這個 trade-off。

🧰 Shell vs AI 的分工我現在怎麼看?

類型 Shell 解析 AI 解析
Git 操作 完全 Shell 處理 AI 不適合
Diff 壓縮 Shell 做最好 AI 用來看結果
程式碼解讀 限制多 AI 更擅長
安全風險判斷 初步過濾 AI 加深分析

💬 簡要完整步驟說明

1. windows排程定期呼叫WSL,執行指令

2. WSL呼叫腳本,腳本使用cursor CLI執行prompt

3. prompt將結果生成檔案存在專案的指定資料夾

4. 在專案寫一支排程,固定抓最新的內容寄信

💬 腳本完整內容

#!/usr/bin/env bash
set -euo pipefail

echo "[1] 開始執行 weekly.sh"

REPO_DIR="/mnt/d/EIP"

echo "[2] REPO_DIR = $REPO_DIR"


cd "$REPO_DIR"
echo "[5] 已切換至 repo 目錄"

echo "[6] 取得最近七天 commit hash…"
COMMIT_HASHES=$(git log \
  --since='8 days ago' \
  --until='yesterday' \
  --pretty=format:'%H')

if [ -z "$COMMIT_HASHES" ]; then
  echo "[7] 最近七天沒有 commit"
  exit 0
fi

# 大 diff 判斷門檻(總新增+刪除行數超過就視為大 diff)
LARGE_DIFF_THRESHOLD=400

# 敏感關鍵字(LV3)
SENSITIVE_PATTERNS='password|passwd|pwd|token|secret|apikey|api_key|authorization|bearer|jwt|connectionstring|datasource|user id=|select |insert |update |delete |drop table|truncate '

echo "[8] 逐筆組合 commit 詳細資訊(含檔案清單、新增/刪除行、敏感候選)…"

COMMITS_DETAIL=""

for HASH in $COMMIT_HASHES; do
  # 基本資訊
  META=$(git log -1 --pretty=format:'%H%n%an%n%ad%n%s' --date=iso "$HASH")
  IFS=$'\n' read -r H AUTHOR DATE_STR MSG <<< "$META"

  # 檔案清單(Name-Status)
  FILES=$(git show --name-status --pretty="" "$HASH")

  # numstat:每檔新增/刪除行數
  NUMSTAT=$(git show --numstat --pretty="" "$HASH" || echo "")
  TOTAL_ADDED=$(printf '%s\n' "$NUMSTAT" | awk '{a+=$1} END {print a+0}')
  TOTAL_DELETED=$(printf '%s\n' "$NUMSTAT" | awk '{d+=$2} END {print d+0}')
  TOTAL_CHANGED=$((TOTAL_ADDED + TOTAL_DELETED))

  # 完整 patch(用來做萃取用,之後再壓縮)
  PATCH=$(git show "$HASH" --patch --unified=3)

  # 判斷是否為大 diff
  IS_LARGE_DIFF=0
  if [ "$TOTAL_CHANGED" -gt "$LARGE_DIFF_THRESHOLD" ]; then
    IS_LARGE_DIFF=1
  fi

  ADDED_LINES=""
  DELETED_LINES=""

  if [ "$IS_LARGE_DIFF" -eq 0 ]; then
    # 小 diff:保留所有新增/刪除行(仍做上限避免極端情況)
    ADDED_LINES=$(printf '%s\n' "$PATCH" \
      | sed -n 's/^+\([^+].*\)$/\1/p' \
      | head -n 200)

    DELETED_LINES=$(printf '%s\n' "$PATCH" \
      | sed -n 's/^-\([^-].*\)$/\1/p' \
      | head -n 200)
  fi

  # 不論大小 diff,都做敏感關鍵字萃取(LV3)
  SENSITIVE_LINES=$(printf '%s\n' "$PATCH" \
  | sed -n 's/^+\([^+].*\)$/[ADDED] \1/p; s/^-\([^-].*\)$/[DELETED] \1/p' \
  | grep -Ei "$SENSITIVE_PATTERNS" \
  | head -n 100 || true)

  COMMITS_DETAIL+=$'\n\n===== COMMIT START =====\n'
  COMMITS_DETAIL+="Commit: $H"$'\n'
  COMMITS_DETAIL+="Author: $AUTHOR"$'\n'
  COMMITS_DETAIL+="Date: $DATE_STR"$'\n'
  COMMITS_DETAIL+="Message: $MSG"$'\n'
  COMMITS_DETAIL+="TotalChangedLines: $TOTAL_CHANGED (+$TOTAL_ADDED / -$TOTAL_DELETED)"$'\n\n'

  COMMITS_DETAIL+="[FILES]\n$FILES"$'\n\n'

  COMMITS_DETAIL+="[LARGE_DIFF_SUMMARY]\n"
  if [ "$IS_LARGE_DIFF" -eq 1 ]; then
    COMMITS_DETAIL+="(此 commit 為大規模變更,只提供統計資訊與敏感候選程式碼,建議人工 Review)\n"
  else
    COMMITS_DETAIL+="(此 commit 為一般規模變更,以下包含完整新增/刪除程式碼摘要)\n"
  fi
  COMMITS_DETAIL+="$NUMSTAT"$'\n\n'

  COMMITS_DETAIL+="[ADDED_LINES]\n"
  if [ -n "$ADDED_LINES" ]; then
    COMMITS_DETAIL+="$ADDED_LINES"$'\n\n'
  else
    COMMITS_DETAIL+="(本次 commit 未列出新增程式碼內容,可能為大 diff 或無新增行)"$'\n\n'
  fi

  COMMITS_DETAIL+="[DELETED_LINES]\n"
  if [ -n "$DELETED_LINES" ]; then
    COMMITS_DETAIL+="$DELETED_LINES"$'\n'
  else
    COMMITS_DETAIL+="(本次 commit 未列出刪除程式碼內容,可能為大 diff 或無刪除行)"$'\n'
  fi

  COMMITS_DETAIL+=$'\n[SENSITIVE_CANDIDATES]\n'
  if [ -n "$SENSITIVE_LINES" ]; then
    COMMITS_DETAIL+="$SENSITIVE_LINES"$'\n'
  else
    COMMITS_DETAIL+="(未從新增/刪除行中偵測到明顯敏感關鍵字)"$'\n'
  fi

  COMMITS_DETAIL+=$'\n===== COMMIT END ====='
done


#echo "[COMMITS_DETAIL]$COMMITS_DETAIL"

CURSOR_AGENT="/root/.local/bin/cursor-agent"

echo "[9] 測試 cursor-agent 是否存在…"
if [ ! -x "$CURSOR_AGENT" ]; then
  echo "[錯誤] 找不到 cursor-agent,請確認路徑:$CURSOR_AGENT"
  exit 1
fi
echo "[10] cursor-agent 版本:"
"$CURSOR_AGENT" --version

# 計算命名用參數
COMMIT_COUNT=$(git -C "$REPO_DIR" log --since='7 days ago' --pretty=oneline | wc -l)
AUTHOR_COUNT=$(git -C "$REPO_DIR" log --since='7 days ago' --format='%an' | sort | uniq | wc -l)
DATETIME=$(date +%Y%m%d%H%M)
OUTPUT_FILE_NAME="${DATETIME}_${COMMIT_COUNT}_${AUTHOR_COUNT}.html"

echo "[11] 呼叫 cursor-agent 產生週報…"

"$CURSOR_AGENT" -p "
你是一個嚴謹的 Git 紀錄與安全風險分析器。

【輸入資料格式】
每筆 commit 以以下格式提供:

===== COMMIT START =====
Commit: <hash>
Author: <作者>
Date: <時間>
Message: <訊息>
TotalChangedLines: <總變更行數> (+新增 / -刪除)

[FILES]
git show --name-status 的輸出

[LARGE_DIFF_SUMMARY]
git show --numstat 的輸出,並註記是否為大規模變更

[ADDED_LINES]
在小規模變更時,提供本次 commit 新增的程式碼行(已移除前綴 +)

[DELETED_LINES]
在小規模變更時,提供本次 commit 刪除的程式碼行(已移除前綴 -)

[SENSITIVE_CANDIDATES]
從新增/刪除行中,依據關鍵字初步篩選出的敏感程式碼片段(例如含 password、token、secret、SQL 語句等),可能為高風險區域
===== COMMIT END =====

【你的任務】
1. 針對本週整體變更,先給一段『總覽』:
   - commit 總數(可從資料推估)
   - 主要修改模組 / 功能
   - 是否有大規模 refactor 或結構性調整

2. 針對每筆 commit:
   - 三行內說明此 commit 的主要目的與影響範圍
   - 依 [FILES] 整理:
     - 新增:xxx
     - 修改:xxx
     - 刪除:xxx

3. 安全與風險檢查:
   - 優先根據 [SENSITIVE_CANDIDATES] 分析是否存在:
     - API Key / 密碼 / Token / 金鑰 等敏感資訊
     - SQL Injection 或可疑 SQL 操作
     - 帳號與權限控制異常
     - 疑似後門 / Log 濫用 / Debug 內容外洩 / 規避審核
   - 若是大 diff(可以從 TotalChangedLines 判斷):
     - 請特別註記『建議人工逐檔 code review』
   - 若無法判定風險,但有可疑片段,請標註「需要人工確認」,並簡述原因。

【輸出格式要求】
- 請輸出一份純 HTML 格式 週報,不要產生任何程式碼。
   - 輸出HTML文章保存於此專案的 /EIP_spec/weekly_reviews
   - 命名為$OUTPUT_FILE_NAME
- 結構建議包含:
  - 本週變更總覽
  - 每筆 commit 摘要(含風險評估)
  - 一個『高風險與需人工確認清單』的小節


【以下是最近七天的 commit 詳細資料】
$COMMITS_DETAIL
"
echo "[12] 已完成"

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言