
一開始,我只想做一件看起來很單純的事:
「我可不可以讓 Cursor 每週自動幫我看 Git 專案,產一份 Markdown 週報,順便掃一下有沒有可疑程式碼?」
腦中想像的畫面是這樣的:
公司的專案放在Gitlab上面,無法使用Github Action。
我原本以為 Cursor 可以像 cron job 一樣,自己定期觸發 Agent。
實際上:
因此真正需要的是:
由「外部腳本」去呼叫 Cursor Agent,而不是讓 Cursor 自己跑。
Cursor 提供的 CLI 安裝方式全部偏向 macOS/Linux:
curl https://cursor.sh/install -sSfL | bash
PowerShell 不吃這種指令。
最後的做法:
這讓整個流程穩定許多。
Windows 的磁碟會掛在 /mnt 之下,例如:
D:\ → /mnt/d
Linux 大小寫敏感,一個字母錯了就整個路徑找不到。
最後靠 ls 一層一層往上確認才找到正確位置。
最初版本做的事很單純:
git log 抓最近 7 天的 commit缺點也很明顯:
因此開始做能力提升。
我一開始直接把 diff 全部丟給 AI,但立刻遇到:
促使我開始思考:
哪些內容真的要給 AI?哪些該在 shell 自己先整理?
透過 git show --numstat 計算每個 commit 的變更行數:
用 grep 自動標記可能危險的地方,例如:
這能有效找出:
每個 commit 會被整理成:
===== COMMIT START =====
Commit: <hash>
Author: <作者>
Date: <時間>
Message: <訊息>
TotalChangedLines: <總變更行數>
[FILES]
<檔案清單>
[LARGE_DIFF_SUMMARY]
<大 diff 的統計資訊>
[ADDED_LINES]
<新增行摘要>
[DELETED_LINES]
<刪除行摘要>
[SENSITIVE_CANDIDATES]
<敏感關鍵字命中的片段>
===== COMMIT END =====
這些內容再一次丟給 Cursor 進行解讀。
CLI 的 Agent 看不到你的 repo,
它只能吃你餵進 prompt 的文字。
方便,但可能會受之後的工具更新影響。
目前我接受這個 trade-off。
| 類型 | Shell 解析 | AI 解析 |
|---|---|---|
| Git 操作 | 完全 Shell 處理 | AI 不適合 |
| Diff 壓縮 | Shell 做最好 | AI 用來看結果 |
| 程式碼解讀 | 限制多 | AI 更擅長 |
| 安全風險判斷 | 初步過濾 | AI 加深分析 |
#!/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] 已完成"