mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Merge 4256e696da7c64418e557275234d264aae61b3b1 into d1e684c419b4f88fee62490b0c2b4fcf65469e59
This commit is contained in:
commit
5cb3b07ffb
35
.env_example_gitcommit
Normal file
35
.env_example_gitcommit
Normal file
@ -0,0 +1,35 @@
|
||||
# Git Commit Script Configuration
|
||||
|
||||
# Debug mode: true or false
|
||||
DEBUG_MODE=false
|
||||
|
||||
# # MiniMax
|
||||
# BASE_URL=https://api.minimaxi.com/v1
|
||||
# API_KEY=your_api_key_here
|
||||
# LLM_MODEL=MiniMax-M2.5
|
||||
|
||||
# # Zhipu AI
|
||||
# BASE_URL=https://open.bigmodel.cn/api/paas/v4
|
||||
# API_KEY=your_api_key_here
|
||||
# LLM_MODEL=GLM-4.7-Flash
|
||||
|
||||
|
||||
# siliconflow
|
||||
# BASE_URL=https://api.siliconflow.cn/v1
|
||||
# API_KEY=your_api_key_here
|
||||
# LLM_MODEL=Qwen/Qwen2.5-Coder-32B-Instruct
|
||||
# LLM_MODEL=Qwen/Qwen3-Coder-30B-A3B-Instruct
|
||||
# LLM_MODEL=Qwen/Qwen3-30B-A3B-Instruct-2507
|
||||
|
||||
## openrouter
|
||||
BASE_URL=https://openrouter.ai/api/v1
|
||||
API_KEY=your_api_key_here
|
||||
LLM_MODEL=minimax/minimax-m2.5:free
|
||||
# LLM_MODEL=nvidia/nemotron-3-super-120b-a12b:free
|
||||
# LLM_MODEL=arcee-ai/trinity-large-preview:free
|
||||
# LLM_MODEL=z-ai/glm-4.5-air:free
|
||||
# LLM_MODEL=nvidia/nemotron-3-nano-30b-a3b:free
|
||||
# LLM_MODEL=minimax/minimax-m2.5:free
|
||||
# LLM_MODEL=openai/gpt-oss-120b:free
|
||||
# LLM_MODEL=openai/gpt-oss-20b:free
|
||||
# LLM_MODEL=google/gemma-4-31b-it:free
|
||||
66
___git_commit_push_llm.sh
Normal file
66
___git_commit_push_llm.sh
Normal file
@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Cross-platform Git Commit and Push Script Router
|
||||
# Auto-detects OS and delegates to the platform-specific implementation
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# ======================================= [全局 .env 文件支持]
|
||||
# Load root-level .env first so platform scripts inherit via environment
|
||||
ENV_FILE="$SCRIPT_DIR/.env"
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "${line// /}" ]] && continue
|
||||
if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
value="${BASH_REMATCH[2]}"
|
||||
value="${value%$'\r'}"
|
||||
value="${value#\"}"; value="${value%\"}"
|
||||
value="${value#\'}"; value="${value%\'}"
|
||||
if ! eval "[ -n \"\${$key+x}\" ]"; then
|
||||
export "$key=$value"
|
||||
fi
|
||||
fi
|
||||
done < "$ENV_FILE"
|
||||
fi
|
||||
|
||||
# ======================================= [平台检测与路由]
|
||||
detect_os() {
|
||||
case "$(uname -s)" in
|
||||
Linux*|Darwin*) echo "linux" ;;
|
||||
CYGWIN*|MINGW*|MSYS*) echo "windows" ;;
|
||||
*) echo "unknown" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
OS=$(detect_os)
|
||||
|
||||
if [ "$OS" = "linux" ]; then
|
||||
TARGET="$SCRIPT_DIR/zbak_script/linux/commit/___git_commit_push_llm.sh"
|
||||
if [ ! -f "$TARGET" ]; then
|
||||
echo "Error: Linux script not found: $TARGET" >&2
|
||||
exit 1
|
||||
fi
|
||||
exec bash "$TARGET"
|
||||
|
||||
elif [ "$OS" = "windows" ]; then
|
||||
TARGET="$SCRIPT_DIR/zbak_script/win/commit/___git_commit_push_llm.ps1"
|
||||
if [ ! -f "$TARGET" ]; then
|
||||
echo "Error: Windows script not found: $TARGET" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Prefer pwsh (PowerShell 7+), fallback to powershell (Windows PowerShell)
|
||||
if command -v pwsh &>/dev/null; then
|
||||
exec pwsh -ExecutionPolicy Bypass -File "$TARGET"
|
||||
elif command -v powershell &>/dev/null; then
|
||||
exec powershell -ExecutionPolicy Bypass -File "$TARGET"
|
||||
else
|
||||
echo "Error: PowerShell (pwsh or powershell) not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Error: Unsupported OS '$(uname -s)'. Expected Linux, macOS, or Windows (Git Bash/MSYS)." >&2
|
||||
exit 1
|
||||
fi
|
||||
141
__cc-haha-op.sh
Normal file
141
__cc-haha-op.sh
Normal file
@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
REPO_ROOT="$(builtin cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd -P)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
# ── Configurable remotes ─────────────────────────────────────────────────────
|
||||
UPSTREAM_REMOTE="${UPSTREAM_REMOTE:-upstream}"
|
||||
ORIGIN_REMOTE="${ORIGIN_REMOTE:-origin}"
|
||||
|
||||
BUILD_SCRIPT="$REPO_ROOT/desktop/scripts/build-windows-x64.ps1"
|
||||
|
||||
# ── Colors ───────────────────────────────────────────────────────────────────
|
||||
red() { echo -e "\033[31m$*\033[0m"; }
|
||||
green() { echo -e "\033[32m$*\033[0m"; }
|
||||
yellow() { echo -e "\033[33m$*\033[0m"; }
|
||||
cyan() { echo -e "\033[36m$*\033[0m"; }
|
||||
|
||||
# ── Sync upstream ────────────────────────────────────────────────────────────
|
||||
sync_upstream() {
|
||||
# 1. Check upstream remote exists, auto-add if missing
|
||||
if ! git remote get-url "$UPSTREAM_REMOTE" > /dev/null 2>&1; then
|
||||
yellow "Remote '$UPSTREAM_REMOTE' not found. Adding..."
|
||||
git remote add "$UPSTREAM_REMOTE" git@github.com:NanmiCoder/cc-haha.git
|
||||
fi
|
||||
|
||||
# 2. Check origin remote exists
|
||||
if ! git remote get-url "$ORIGIN_REMOTE" > /dev/null 2>&1; then
|
||||
red "Error: remote '$ORIGIN_REMOTE' not found."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 3. Stash if worktree is dirty
|
||||
local stashed=false
|
||||
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
|
||||
yellow "Worktree is dirty. Stashing changes..."
|
||||
git stash push -m "auto: stash before sync $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
stashed=true
|
||||
fi
|
||||
|
||||
# 4. Determine original branch
|
||||
local original_branch
|
||||
local current_branch
|
||||
current_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
local all_branches
|
||||
all_branches=$(git branch --list --format='%(refname:short)')
|
||||
|
||||
# If only on main with no other local branches, create a new branch
|
||||
if [ "$current_branch" = "main" ] && [ -z "$(echo "$all_branches" | grep -v '^main$')" ]; then
|
||||
local new_branch="feature/sync-$(date '+%Y%m%d')"
|
||||
yellow "Only main branch found. Creating new branch: $new_branch"
|
||||
git checkout -b "$new_branch"
|
||||
git add -A
|
||||
git commit -m "chore: initial commit on $new_branch"
|
||||
original_branch="$new_branch"
|
||||
else
|
||||
original_branch="$current_branch"
|
||||
fi
|
||||
|
||||
# 5. Checkout main, fetch upstream, merge
|
||||
echo "[1/4] Fetching $UPSTREAM_REMOTE..."
|
||||
git fetch "$UPSTREAM_REMOTE"
|
||||
|
||||
echo "[2/4] Checking out main and merging $UPSTREAM_REMOTE/main..."
|
||||
git checkout main
|
||||
git merge "$UPSTREAM_REMOTE/main" --no-edit
|
||||
|
||||
# 6. Push to origin
|
||||
echo "[3/4] Pushing to $ORIGIN_REMOTE..."
|
||||
git push "$ORIGIN_REMOTE" main
|
||||
|
||||
# 7. Checkout original branch and rebase onto main
|
||||
echo "[4/4] Rebasing $original_branch onto main..."
|
||||
git checkout "$original_branch"
|
||||
if ! git rebase main; then
|
||||
red "Rebase failed. Resolve conflicts, then run:"
|
||||
echo " git rebase --continue"
|
||||
echo " git stash pop (if you had stashed changes)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 8. Pop stash if we stashed
|
||||
if [ "$stashed" = true ]; then
|
||||
echo "Restoring stashed changes..."
|
||||
git stash pop
|
||||
fi
|
||||
|
||||
green "Sync complete."
|
||||
}
|
||||
|
||||
# ── Build exe ────────────────────────────────────────────────────────────────
|
||||
build_exe() {
|
||||
echo "[build-exe] 执行 PowerShell 构建脚本..."
|
||||
if command -v pwsh > /dev/null 2>&1; then
|
||||
pwsh -ExecutionPolicy Bypass -File "$BUILD_SCRIPT"
|
||||
elif command -v powershell > /dev/null 2>&1; then
|
||||
powershell -ExecutionPolicy Bypass -File "$BUILD_SCRIPT"
|
||||
else
|
||||
red "错误:未找到 pwsh 或 powershell"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Menu ─────────────────────────────────────────────────────────────────────
|
||||
show_menu() {
|
||||
clear
|
||||
echo "========== cc-haha menu =========="
|
||||
echo "1. sync-upstream (fetch → merge → push → rebase)"
|
||||
echo "2. build-exe"
|
||||
echo "0. exit"
|
||||
echo "=================================="
|
||||
}
|
||||
# ── Main ─────────────────────────────────────────────────────────────────────
|
||||
main() {
|
||||
case "${1:-}" in
|
||||
sync|1)
|
||||
sync_upstream
|
||||
;;
|
||||
build|2)
|
||||
build_exe
|
||||
;;
|
||||
*)
|
||||
while true; do
|
||||
show_menu
|
||||
read -r -p "请选择: " choice || { echo ""; exit 0; }
|
||||
case "$choice" in
|
||||
1) sync_upstream ;;
|
||||
2) build_exe ;;
|
||||
0) echo "Bye."; exit 0 ;;
|
||||
*) red "无效选项,请重新输入。" ;;
|
||||
esac
|
||||
|
||||
if [ "$choice" != "0" ]; then
|
||||
echo ""
|
||||
read -r -p "按 Enter 继续..."
|
||||
fi
|
||||
done
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@ -69,7 +69,9 @@ export function extractInboundPayload(content: string, msgType: string): Inbound
|
||||
}
|
||||
|
||||
if (msgType === 'post') {
|
||||
const nodes = (parsed.zh_cn?.content ?? parsed.en_us?.content ?? []) as any[]
|
||||
// Post content can either be multi-locale ({zh_cn:{content:[...]}, en_us:...})
|
||||
// or single-locale ({title:"", content:[[...],...]}). Try both.
|
||||
const nodes = (parsed.zh_cn?.content ?? parsed.en_us?.content ?? parsed.content ?? []) as any[]
|
||||
const flat = nodes.flat()
|
||||
const textParts: string[] = []
|
||||
const downloads: PendingDownload[] = []
|
||||
|
||||
@ -936,6 +936,7 @@ function stripMentions(text: string): string {
|
||||
// ---------- event handlers ----------
|
||||
|
||||
async function handleMessage(data: any): Promise<void> {
|
||||
|
||||
const event = data as {
|
||||
sender?: { sender_id?: { open_id?: string } }
|
||||
message?: {
|
||||
|
||||
351
zbak_script/linux/commit/___git_commit_push_llm.sh
Normal file
351
zbak_script/linux/commit/___git_commit_push_llm.sh
Normal file
@ -0,0 +1,351 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Auto Git Commit and Push Script by BeyondTS
|
||||
# 智谱AI邀请:https://www.bigmodel.cn/invite?icode=i1hBPobA%2Bmtg8XS3qmwhTUjPr3uHog9F4g5tjuOUqno%3D
|
||||
# MiniMax邀请:https://platform.minimaxi.com/subscribe/coding-plan?code=2685TjN0NW&source=link
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# ======================================= [本地 .env 文件支持]
|
||||
ENV_FILE="$SCRIPT_DIR/.env"
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "${line// /}" ]] && continue
|
||||
if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
value="${BASH_REMATCH[2]}"
|
||||
value="${value#\"}"; value="${value%\"}"
|
||||
value="${value#\'}"; value="${value%\'}"
|
||||
if ! eval "[ -n \"\${$key+x}\" ]"; then
|
||||
export "$key=$value"
|
||||
fi
|
||||
fi
|
||||
done < "$ENV_FILE"
|
||||
fi
|
||||
# ======================================= [调试选项]
|
||||
DEBUG_MODE=${DEBUG_MODE:-false}
|
||||
# ======================================= [KDE Wallet 集成]
|
||||
KWALLET_NAME="kdewallet"
|
||||
KWALLET_FOLDER="api_keys"
|
||||
KWALLET_ENTRY="SILICON_API_KEY"
|
||||
# ======================================= [LLM配置]
|
||||
BASE_URL=${BASE_URL:-"https://api.siliconflow.cn/v1"}
|
||||
LLM_MODEL=${LLM_MODEL:-"Qwen/Qwen3-30B-A3B-Instruct-2507"}
|
||||
|
||||
# 从 KDE Wallet 获取 API Key(需钱包已解锁)
|
||||
get_api_key_from_kwallet() {
|
||||
if ! command -v kwalletcli &>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
local api_key
|
||||
api_key=$(kwalletcli -e "$KWALLET_ENTRY" -f "$KWALLET_FOLDER" 2>/dev/null)
|
||||
|
||||
if [[ $? -eq 0 && -n "$api_key" ]]; then
|
||||
echo "$api_key"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 获取 API Key(优先 KDE Wallet,其次环境变量 / .env 文件)
|
||||
apiKey=$(get_api_key_from_kwallet)
|
||||
if [ -z "$apiKey" ]; then
|
||||
apiKey="${SILICON_API_KEY:-${API_KEY:-}}"
|
||||
fi
|
||||
# Check API key before proceeding
|
||||
if [ -z "$apiKey" ]; then
|
||||
echo -e "${RED}Error: API key is not configured.${NC}" >&2
|
||||
echo -e "${YELLOW}Please ensure one of the following:${NC}" >&2
|
||||
echo -e " 1. KDE Wallet is unlocked and entry exists:" >&2
|
||||
echo -e " Wallet: $KWALLET_NAME, Folder: $KWALLET_FOLDER, Entry: $KWALLET_ENTRY" >&2
|
||||
echo -e " 2. Create a .env file next to this script with:${NC}" >&2
|
||||
echo -e " SILICON_API_KEY=your_api_key" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
LLM_PROMPT_TEMPLATE='根据Git diff生成commit message。禁止任何分析、解释、说明。
|
||||
Git Diff:
|
||||
%s
|
||||
输出格式:
|
||||
{描述}:
|
||||
- {文件1}: {改动1}
|
||||
- {文件2}: {改动2}
|
||||
...
|
||||
参数说明:
|
||||
描述: 20字内,动词开头,禁止废话,
|
||||
文件n: 相对路径全名(文件1 != 文件2 != 文件3...)
|
||||
改动: 50字内,动词开头,禁止废话
|
||||
注意每一个 - {文件n}: {改动n}单独占用一行,也不要有空行'
|
||||
|
||||
# Function to check and clear any existing Git processes and lock files
|
||||
clear_git_processes() {
|
||||
# Check for running git processes
|
||||
echo -e "${CYAN}Checking for existing Git processes...${NC}"
|
||||
git_pids=$(pgrep -x git 2>/dev/null)
|
||||
|
||||
if [ -n "$git_pids" ]; then
|
||||
echo -e "${YELLOW}Found running Git process(es). Attempting to clear...${NC}"
|
||||
echo "$git_pids" | while read pid; do
|
||||
echo -e "${YELLOW} Stopping Git process (PID: $pid)...${NC}"
|
||||
# Disown the process first to prevent "Killed" message from shell
|
||||
if jobs -p | grep -q "^${pid}$" 2>/dev/null; then
|
||||
disown "$pid" 2>/dev/null
|
||||
fi
|
||||
kill -9 "$pid" 2>/dev/null
|
||||
done
|
||||
sleep 2
|
||||
|
||||
remaining_git_pids=$(pgrep -x git 2>/dev/null)
|
||||
if [ -n "$remaining_git_pids" ]; then
|
||||
echo -e "${RED}Warning: Could not stop all Git processes. Some operations might fail.${NC}"
|
||||
else
|
||||
echo -e "${GREEN}All Git processes cleared successfully.${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}No existing Git processes detected.${NC}"
|
||||
fi
|
||||
|
||||
# Check and remove git lock files
|
||||
echo -e "${CYAN}Checking for Git lock files...${NC}"
|
||||
gitDir=".git"
|
||||
lockFiles=()
|
||||
|
||||
# Index lock file
|
||||
if [ -f "$gitDir/index.lock" ]; then
|
||||
lockFiles+=("$gitDir/index.lock")
|
||||
fi
|
||||
|
||||
# Check for other possible lock files in .git directory
|
||||
if [ -d "$gitDir" ]; then
|
||||
while IFS= read -r file; do
|
||||
lockFiles+=("$file")
|
||||
done < <(find "$gitDir" -name "*.lock" -type f 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Remove lock files if found
|
||||
if [ ${#lockFiles[@]} -gt 0 ]; then
|
||||
echo -e "${YELLOW}Found ${#lockFiles[@]} Git lock file(s). Removing...${NC}"
|
||||
for lockFile in "${lockFiles[@]}"; do
|
||||
echo -e "${YELLOW} Removing lock file: $lockFile${NC}"
|
||||
rm -f "$lockFile"
|
||||
if [ -f "$lockFile" ]; then
|
||||
echo -e "${RED} Failed to remove lock file: $lockFile${NC}"
|
||||
else
|
||||
echo -e "${GREEN} Successfully removed lock file: $lockFile${NC}"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo -e "${GREEN}No Git lock files detected.${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to get git diff in a structured format
|
||||
get_git_changes() {
|
||||
stagedFiles=$(git diff --staged --name-status)
|
||||
if [ -z "$stagedFiles" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
added=()
|
||||
modified=()
|
||||
deleted=()
|
||||
|
||||
while IFS= read -r line; do
|
||||
status=$(echo "$line" | cut -c1)
|
||||
file=$(echo "$line" | cut -c3-)
|
||||
case "$status" in
|
||||
A) added+=("$file") ;;
|
||||
M) modified+=("$file") ;;
|
||||
D) deleted+=("$file") ;;
|
||||
esac
|
||||
done <<< "$stagedFiles"
|
||||
}
|
||||
|
||||
# Function to get detailed diff content
|
||||
get_detailed_diff() {
|
||||
diff=$(git diff --staged --patch)
|
||||
if [ -z "$diff" ]; then
|
||||
echo "No changes detected"
|
||||
else
|
||||
echo "$diff"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to generate commit message using LLM
|
||||
get_llm_commit_message() {
|
||||
local diffContent="$1"
|
||||
local prompt=$(printf "$LLM_PROMPT_TEMPLATE" "$diffContent")
|
||||
|
||||
local maxRetries=3
|
||||
local retryCount=0
|
||||
|
||||
while [ $retryCount -lt $maxRetries ]; do
|
||||
echo -e "${CYAN}Model in use: $LLM_MODEL${NC}" >&2
|
||||
if [ -z "$apiKey" ]; then
|
||||
echo -e "${RED}Error: Environment variable MINIMAX_API_KEY is not set.${NC}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Prepare JSON payload via stdin (avoids cmd length limits on Windows)
|
||||
jsonPayload=$(printf '%s\x00%s' "$prompt" "$LLM_MODEL" | python3 -c "import json,sys; p,m=sys.stdin.read().split('\x00'); print(json.dumps({'model':m,'messages':[{'role':'user','content':p}],'top_p':0.7,'temperature':0.9},ensure_ascii=False))")
|
||||
|
||||
apiUrl="${BASE_URL}/chat/completions"
|
||||
|
||||
response=$(curl -s -X POST "$apiUrl" \
|
||||
-H "Authorization: Bearer $apiKey" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$jsonPayload" 2>/dev/null)
|
||||
|
||||
if [ $? -eq 0 ] && [ -n "$response" ]; then
|
||||
# Extract response content using Python via stdin
|
||||
commitMessage=$(printf '%s' "$response" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('choices',[{}])[0].get('message',{}).get('content','') or '')")
|
||||
|
||||
if [ -n "$commitMessage" ]; then
|
||||
# Debug: print raw message before filtering
|
||||
if [ "$DEBUG_MODE" = true ]; then
|
||||
echo -e "${YELLOW}[Debug] Raw LLM response before filtering:${NC}" >&2
|
||||
echo "$commitMessage" >&2
|
||||
echo "" >&2
|
||||
fi
|
||||
|
||||
# Step 1: Remove content between <think> and </think> tags (including the tags)
|
||||
commitMessage=$(echo "$commitMessage" | sed -E '/<think>/,/<\/think>/d')
|
||||
|
||||
# Step 2: Keep lines that look like commit message:
|
||||
# - Description line: ends with : or : but doesn't contain analysis keywords
|
||||
# - File list line: starts with -
|
||||
commitMessage=$(echo "$commitMessage" | grep -E '(^\s*- |[::]$)' | grep -vE '(应该|需要|是|关键|主要|分析|按照|根据|让我|描述|文件|改动|变化)[::]$')
|
||||
# Step 3: Remove template placeholder lines like {描述}: {文件1}:
|
||||
commitMessage=$(echo "$commitMessage" | sed -E '/\{[^}]+\}[::]$/d')
|
||||
|
||||
# Step 4: Clean up whitespace
|
||||
commitMessage=$(echo "$commitMessage" | sed -E 's/^[ \t]+//')
|
||||
commitMessage=$(echo "$commitMessage" | sed '/./,$!d' | tac | sed '/./,$!d' | tac)
|
||||
|
||||
# Debug: print after filtering
|
||||
if [ "$DEBUG_MODE" = true ]; then
|
||||
echo -e "${YELLOW}[Debug] After filtering:${NC}" >&2
|
||||
echo "$commitMessage" >&2
|
||||
echo "" >&2
|
||||
fi
|
||||
|
||||
echo "$commitMessage"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
retryCount=$((retryCount + 1))
|
||||
if [ $retryCount -lt $maxRetries ]; then
|
||||
echo -e "${YELLOW}LLM request failed, retrying ($retryCount/$maxRetries)...${NC}" >&2
|
||||
sleep $((2 * retryCount))
|
||||
else
|
||||
echo -e "${RED}Failed to generate LLM commit message after $maxRetries retries${NC}" >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Main script execution
|
||||
|
||||
# Clear any existing Git processes and lock files before proceeding
|
||||
clear_git_processes
|
||||
|
||||
# Auto stage all changes
|
||||
echo ""
|
||||
echo -e "${CYAN}Automatically staging all changes...${NC}"
|
||||
git add -A
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}Failed to stage files${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}Files staged successfully${NC}"
|
||||
|
||||
# Check staged changes
|
||||
echo ""
|
||||
echo -e "${CYAN}Starting to check Git changes...${NC}"
|
||||
get_git_changes
|
||||
if [ $? -ne 0 ]; then
|
||||
# 检查是否有未推送的提交
|
||||
unpushed=$(git log --branches --not --remotes --oneline 2>/dev/null)
|
||||
if [ -n "$unpushed" ]; then
|
||||
echo -e "${YELLOW}No new changes to commit, but found unpushed commits:${NC}"
|
||||
echo "$unpushed"
|
||||
echo ""
|
||||
echo -e "${CYAN}Pushing unpushed commits to remote...${NC}"
|
||||
if git push --all origin 2>&1; then
|
||||
echo -e "${GREEN}Commits pushed successfully${NC}"
|
||||
echo '{"success":true,"message":"Unpushed commits pushed successfully"}'
|
||||
else
|
||||
echo -e "${RED}错误:远程推送失败${NC}" >&2
|
||||
echo '{"success":false,"message":"Push failed"}'
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN}No changes detected, nothing to commit.${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}Auto Git commit and push completed with no changes!${NC}"
|
||||
echo '{"success":true,"message":"No changes to commit, working tree clean"}'
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
diffContent=$(get_detailed_diff)
|
||||
|
||||
# Generate commit message using LLM
|
||||
echo ""
|
||||
echo -e "${CYAN}Using LLM to analyze changes and generate a commit message...${NC}"
|
||||
commitMessage=$(get_llm_commit_message "$diffContent")
|
||||
|
||||
if [ -z "$commitMessage" ]; then
|
||||
currentDate=$(date "+%Y-%m-%d %H:%M:%S")
|
||||
commitMessage="LLM invalid, auto backup: $currentDate"
|
||||
echo -e "${YELLOW}Using default commit message${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}Commit message:${NC}"
|
||||
echo -e "${GREEN}$commitMessage${NC}"
|
||||
|
||||
if [ "$DEBUG_MODE" = true ]; then
|
||||
echo ""
|
||||
echo -e "${YELLOW}[Debug mode] Skipping commit and push operations${NC}"
|
||||
else
|
||||
# Perform commit operation
|
||||
echo ""
|
||||
echo -e "${CYAN}Creating a commit...${NC}"
|
||||
# Write commit message to temp file to avoid quoting issues
|
||||
commitMsgFile=$(mktemp)
|
||||
echo "$commitMessage" > "$commitMsgFile"
|
||||
if git commit -F "$commitMsgFile" 2>&1; then
|
||||
rm -f "$commitMsgFile"
|
||||
echo -e "${GREEN}Commit created successfully${NC}"
|
||||
else
|
||||
rm -f "$commitMsgFile"
|
||||
echo -e "${RED}错误:提交失败${NC}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}Pushing all branches to remote...${NC}"
|
||||
if git push --all origin 2>&1; then
|
||||
echo -e "${GREEN}Branches pushed successfully${NC}"
|
||||
else
|
||||
echo -e "${RED}错误:远程推送失败,但提交已创建${NC}" >&2
|
||||
echo '{"success":false,"message":"Push failed but commit created"}'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Auto Git commit and push completed!${NC}"
|
||||
echo '{"success":true,"message":"Auto Git commit and push completed!"}'
|
||||
321
zbak_script/win/commit/___git_commit_push_llm.ps1
Normal file
321
zbak_script/win/commit/___git_commit_push_llm.ps1
Normal file
@ -0,0 +1,321 @@
|
||||
# Auto Git Commit and Push Script by BeyondTS
|
||||
using namespace System.Web
|
||||
# 智谱AI邀请:https://www.bigmodel.cn/invite?icode=i1hBPobA%2Bmtg8XS3qmwhTUjPr3uHog9F4g5tjuOUqno%3D
|
||||
# MiniMax邀请:https://platform.minimaxi.com/subscribe/coding-plan?code=2685TjN0NW&source=link
|
||||
|
||||
# Load configuration from .env file if it exists
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$envFile = Join-Path $scriptDir ".env"
|
||||
if (Test-Path $envFile) {
|
||||
Get-Content $envFile | ForEach-Object {
|
||||
if ($_ -match '^\s*([^#][^=]*)\s*=\s*(.*?)\s*$') {
|
||||
$key = $matches[1].Trim()
|
||||
$value = $matches[2].Trim() -replace "`r",""
|
||||
# Remove surrounding quotes if present
|
||||
if ($value -match '^["''](.*)["'']$') {
|
||||
$value = $matches[1]
|
||||
}
|
||||
Set-Item -Path "env:$key" -Value $value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Default values (will be overridden by .env if set)
|
||||
$DEBUG_MODE = if ($env:DEBUG_MODE -eq "true") { $true } else { $false }
|
||||
$BASE_URL = if ($env:BASE_URL) { $env:BASE_URL } else { "https://api.minimaxi.com/v1" }
|
||||
$LLM_MODEL = if ($env:LLM_MODEL) { $env:LLM_MODEL } else { "MiniMax-M2.5" }
|
||||
$apiKey = if ($env:API_KEY) { $env:API_KEY.Trim() } else { $env:MINIMAX_API_KEY.Trim() }
|
||||
|
||||
# Check API key before proceeding
|
||||
if (-not $apiKey) {
|
||||
Write-Host "Error: API key is not configured." -ForegroundColor Red
|
||||
Write-Host "Please set API_KEY or MINIMAX_API_KEY in .env file or environment variables." -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
$gitCmdPath = "C:\Program Files\Git\cmd"
|
||||
$gitCmdPathAlt = "C:\Program Files (x86)\Git\cmd"
|
||||
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
||||
if (Test-Path $gitCmdPath) {
|
||||
$env:PATH = "$gitCmdPath;$env:PATH"
|
||||
} elseif (Test-Path $gitCmdPathAlt) {
|
||||
$env:PATH = "$gitCmdPathAlt;$env:PATH"
|
||||
}
|
||||
}
|
||||
$LLM_PROMPT_TEMPLATE = @"
|
||||
Generate a commit message for the Git diff below.
|
||||
|
||||
OUTPUT FORMAT (exactly follow this format):
|
||||
Summary: One-sentence summary
|
||||
- [Add] filename
|
||||
- [Modify] filename: changes
|
||||
- [Delete] filename
|
||||
|
||||
IMPORTANT:
|
||||
- Do NOT include any explanation, reasoning, or analysis
|
||||
- Do NOT use thinking tags like <thinking>, (, (
|
||||
- Do NOT include "Change content:" or diff details
|
||||
- Output ONLY the commit message, nothing else
|
||||
- Use present continuous tense
|
||||
|
||||
Git Diff:
|
||||
{0}
|
||||
"@
|
||||
|
||||
# Function to check and clear any existing Git processes and lock files
|
||||
function Clear-GitProcesses {
|
||||
try {
|
||||
# Check for running git processes
|
||||
Write-Host "Checking for existing Git processes..." -ForegroundColor Cyan
|
||||
$gitProcesses = Get-Process -Name "git" -ErrorAction SilentlyContinue
|
||||
|
||||
if ($gitProcesses -and $gitProcesses.Count -gt 0) {
|
||||
Write-Host "Found $($gitProcesses.Count) running Git process(es). Attempting to clear..." -ForegroundColor Yellow
|
||||
|
||||
# Try to gracefully stop git processes
|
||||
$gitProcesses | ForEach-Object {
|
||||
Write-Host " Stopping Git process (PID: $($_.Id))..." -ForegroundColor Yellow
|
||||
$_ | Stop-Process -Force
|
||||
}
|
||||
|
||||
# Wait a moment to ensure processes are terminated
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
# Check if any Git processes still exist
|
||||
$remainingGitProcesses = Get-Process -Name "git" -ErrorAction SilentlyContinue
|
||||
if ($remainingGitProcesses -and $remainingGitProcesses.Count -gt 0) {
|
||||
Write-Host "Warning: Could not stop all Git processes. Some operations might fail." -ForegroundColor Red
|
||||
} else {
|
||||
Write-Host "All Git processes cleared successfully." -ForegroundColor Green
|
||||
}
|
||||
} else {
|
||||
Write-Host "No existing Git processes detected." -ForegroundColor Green
|
||||
}
|
||||
|
||||
# Check and remove git lock files
|
||||
Write-Host "Checking for Git lock files..." -ForegroundColor Cyan
|
||||
$gitDir = ".git"
|
||||
$lockFiles = @()
|
||||
|
||||
# Index lock file
|
||||
$indexLock = Join-Path -Path $gitDir -ChildPath "index.lock"
|
||||
if (Test-Path $indexLock) {
|
||||
$lockFiles += $indexLock
|
||||
}
|
||||
|
||||
# Check for other possible lock files in .git directory
|
||||
if (Test-Path $gitDir) {
|
||||
$otherLockFiles = Get-ChildItem -Path $gitDir -Recurse -Filter "*.lock" | Select-Object -ExpandProperty FullName
|
||||
$lockFiles += $otherLockFiles
|
||||
}
|
||||
|
||||
# Remove lock files if found
|
||||
if ($lockFiles.Count -gt 0) {
|
||||
Write-Host "Found $($lockFiles.Count) Git lock file(s). Removing..." -ForegroundColor Yellow
|
||||
|
||||
foreach ($lockFile in $lockFiles) {
|
||||
try {
|
||||
Write-Host " Removing lock file: $lockFile" -ForegroundColor Yellow
|
||||
Remove-Item -Path $lockFile -Force
|
||||
|
||||
if (Test-Path $lockFile) {
|
||||
Write-Host " Failed to remove lock file: $lockFile" -ForegroundColor Red
|
||||
} else {
|
||||
Write-Host " Successfully removed lock file: $lockFile" -ForegroundColor Green
|
||||
}
|
||||
} catch {
|
||||
$errorMsg = $_.Exception.Message
|
||||
Write-Host " Error removing lock file $lockFile - $errorMsg" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Host "No Git lock files detected." -ForegroundColor Green
|
||||
}
|
||||
} catch {
|
||||
$errorMsg = $_.Exception.Message
|
||||
Write-Host "Error checking/clearing Git processes and lock files - $errorMsg" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
# Function to get git diff in a structured format
|
||||
function Get-GitChanges {
|
||||
$stagedFiles = git diff --staged --name-status
|
||||
if (-not $stagedFiles) {
|
||||
return $null
|
||||
}
|
||||
$changes = @{
|
||||
"added" = @()
|
||||
"modified" = @()
|
||||
"deleted" = @()
|
||||
}
|
||||
$stagedFiles | ForEach-Object {
|
||||
$status, $file = $_ -split "\s+"
|
||||
switch ($status) {
|
||||
"A" { $changes["added"] += $file }
|
||||
"M" { $changes["modified"] += $file }
|
||||
"D" { $changes["deleted"] += $file }
|
||||
}
|
||||
}
|
||||
return $changes
|
||||
}
|
||||
|
||||
# Function to get detailed diff content
|
||||
function Get-DetailedDiff {
|
||||
try {
|
||||
$diff = git diff --staged --patch
|
||||
if (-not $diff) {
|
||||
return "No changes detected"
|
||||
}
|
||||
# Convert diff output to string and escape special characters
|
||||
return [string]::Join("
|
||||
", $diff)
|
||||
}
|
||||
catch {
|
||||
Write-Host "Error getting Git diff content: $($_.Exception.Message)" -ForegroundColor Yellow
|
||||
return "Error getting diff content"
|
||||
}
|
||||
}
|
||||
|
||||
# Function to generate commit message using Ollama
|
||||
function Get-LLMCommitMessage {
|
||||
param (
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$diffContent
|
||||
)
|
||||
$prompt = $LLM_PROMPT_TEMPLATE -f $diffContent
|
||||
$maxRetries = 3
|
||||
$retryCount = 0
|
||||
while ($retryCount -lt $maxRetries) {
|
||||
try {
|
||||
Write-Host "Model in use: $LLM_MODEL" -ForegroundColor Cyan
|
||||
if (-not $apiKey) {
|
||||
Write-Host "Error: Environment variable API_KEY is not set." -ForegroundColor Red
|
||||
return $null
|
||||
}
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $apiKey"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
$body = @{
|
||||
model = $LLM_MODEL
|
||||
messages = @(
|
||||
@{role = "user"; content = $prompt}
|
||||
)
|
||||
top_p = 0.7
|
||||
temperature = 0.9
|
||||
} | ConvertTo-Json -Depth 5 # Ensure nested structures are correctly converted
|
||||
$apiUrl = "$BASE_URL/chat/completions"
|
||||
$response = Invoke-RestMethod -Uri $apiUrl -Method Post -Headers $headers -Body $body -ContentType "application/json"
|
||||
if ($response -and $response.choices -and $response.choices.count -gt 0) {
|
||||
# Extract response content
|
||||
$commitMessage = $response.choices[0].message.content
|
||||
|
||||
# Remove AI reasoning tags (<think> and</think>)
|
||||
$commitMessage = $commitMessage -replace '(?s)<thinking>.*?</thinking>', ''
|
||||
$commitMessage = $commitMessage -replace '(?s)<think>.*?</think>', ''
|
||||
$commitMessage = $commitMessage -replace '(?s)<analysis>.*?</analysis>', ''
|
||||
$commitMessage = $commitMessage -replace '(?s)<reasoning>.*?</reasoning>', ''
|
||||
|
||||
# Maintain line breaks and clean up excess whitespace
|
||||
# 修正正则表达式:仅删除行首空白字符(空格/制表符)
|
||||
$cleanResponse = $commitMessage.Trim() -replace '(?m)^[ \t]+', ''
|
||||
# Remove possible prefixes (adjust as needed)
|
||||
# 精确处理commit message前缀(严格匹配格式)
|
||||
$cleanResponse = $cleanResponse -replace '(?im)^commit message:\s*', ''
|
||||
# Extract and format summary content (adjust as needed)
|
||||
# Improved regex to handle case-insensitivity and find the end of the summary line more reliably
|
||||
# 精确匹配并保留原始Summary格式
|
||||
if ($cleanResponse -match '(?m)^Summary:\s*(.*?)(\r?\n|$)') {
|
||||
$summary = $matches[1].Trim()
|
||||
# 直接使用原始匹配内容保持格式
|
||||
$cleanResponse = $cleanResponse -replace '(?m)^Summary:\s*.*?(\r?\n|$)', ''
|
||||
$cleanResponse = "Summary: $summary`r`n" + $cleanResponse.TrimStart()
|
||||
}
|
||||
# Ensure list items start on new lines, handling potential leading spaces
|
||||
$cleanResponse = $cleanResponse -replace '(?<!\r?\n)\s*-\s*\[', "`r`n- ["
|
||||
# If there are blank lines, delete them
|
||||
$cleanResponse = $cleanResponse -replace '(?m)^\s*$\r?\n', ''
|
||||
# Ensure proper UTF-8 encoding without HTML escaping (PowerShell handles this)
|
||||
# $cleanResponse = $cleanResponse # No change needed
|
||||
# Write-Host $cleanResponse -ForegroundColor Green # Debugging line
|
||||
return $cleanResponse
|
||||
}
|
||||
throw "No valid response choices from LLM"
|
||||
}
|
||||
catch {
|
||||
$retryCount++
|
||||
if ($retryCount -lt $maxRetries) {
|
||||
Write-Host "LLM request failed, retrying ($retryCount/$maxRetries)... Error: $($_.Exception.Message)" -ForegroundColor Yellow # Include error message
|
||||
Start-Sleep -Seconds (2 * $retryCount) # Exponential backoff
|
||||
}
|
||||
else {
|
||||
Write-Host "Failed to generate LLM commit message after $maxRetries retries: $($_.Exception.Message)" -ForegroundColor Red # Changed color to Red
|
||||
return $null
|
||||
}
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# Main script execution
|
||||
try {
|
||||
# Clear any existing Git processes and lock files before proceeding
|
||||
Clear-GitProcesses
|
||||
|
||||
# Auto stage all changes
|
||||
Write-Host "`nAutomatically staging all changes..." -ForegroundColor Cyan
|
||||
git add -A
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Failed to stage files"
|
||||
}
|
||||
Write-Host "Files staged successfully" -ForegroundColor Green
|
||||
|
||||
# Check staged changes
|
||||
Write-Host "`nStarting to check Git changes..." -ForegroundColor Cyan
|
||||
$changes = Get-GitChanges
|
||||
if (-not $changes) {
|
||||
Write-Host "No changes detected, nothing to commit." -ForegroundColor Green
|
||||
Write-Host "`nAuto Git commit and push completed with no changes!" -ForegroundColor Green
|
||||
# 不退出,返回成功信息而非直接退出
|
||||
return @{
|
||||
success = $true
|
||||
message = "No changes to commit, working tree clean"
|
||||
}
|
||||
}
|
||||
$diffContent = Get-DetailedDiff
|
||||
# Generate commit message using LLM
|
||||
Write-Host "`nUsing LLM to analyze changes and generate a commit message..." -ForegroundColor Cyan
|
||||
$commitMessage = Get-LLMCommitMessage -diffContent $diffContent
|
||||
if (-not $commitMessage) {
|
||||
$currentDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$commitMessage = "LLM invalid, auto backup: $currentDate"
|
||||
Write-Host "Using default commit message" -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host "`nCommit message:" -ForegroundColor Cyan
|
||||
Write-Host $commitMessage -ForegroundColor Green
|
||||
if ($DEBUG_MODE) {
|
||||
Write-Host "`n[Debug mode] Skipping commit and push operations" -ForegroundColor Yellow
|
||||
# Pause
|
||||
} else {
|
||||
# Perform commit operation
|
||||
Write-Host "`nCreating a commit..." -ForegroundColor Cyan
|
||||
# 将提交消息写入临时文件,避免命令行参数引用问题
|
||||
$commitMsgFile = [System.IO.Path]::GetTempFileName()
|
||||
$commitMessage | Out-File -FilePath $commitMsgFile -Encoding utf8
|
||||
git commit -F $commitMsgFile > $null
|
||||
Remove-Item $commitMsgFile -Force
|
||||
Write-Host "Commit created successfully" -ForegroundColor Green
|
||||
Write-Host "`nPushing all branches to remote..." -ForegroundColor Cyan
|
||||
git push --all origin > $null 2>&1 #do not show messages
|
||||
Write-Host "Branches pushed successfully" -ForegroundColor Green
|
||||
# Pause
|
||||
}
|
||||
|
||||
Write-Host "`nAuto Git commit and push completed!" -ForegroundColor Green
|
||||
Write-Output '{"success":true,"message":"Auto Git commit and push completed!"}'
|
||||
}
|
||||
catch {
|
||||
Write-Host "`nError: $_" -ForegroundColor Red
|
||||
Write-Host "Auto Git commit and push failed" -ForegroundColor Red
|
||||
Write-Output ('{"success":false,"message":"Auto Git commit and push failed: ' + $_.ToString().Replace('"', '\"') + '"}')
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user