From 4256e696da7c64418e557275234d264aae61b3b1 Mon Sep 17 00:00:00 2001 From: burnlife Date: Fri, 8 May 2026 15:48:38 +0800 Subject: [PATCH] feat(feishu): support single-locale post content + add commit helper scripts - Handle Feishu post messages with flat content structure (fallback to parsed.content) - add __cc-haha-op.sh for easy devops - Add cross-platform commit helpers (bash/ps1) for LLM-generated messages --- .env_example_gitcommit | 35 ++ ___git_commit_push_llm.sh | 66 ++++ __cc-haha-op.sh | 141 +++++++ adapters/feishu/extract-payload.ts | 4 +- adapters/feishu/index.ts | 1 + .../linux/commit/___git_commit_push_llm.sh | 351 ++++++++++++++++++ .../win/commit/___git_commit_push_llm.ps1 | 321 ++++++++++++++++ 7 files changed, 918 insertions(+), 1 deletion(-) create mode 100644 .env_example_gitcommit create mode 100644 ___git_commit_push_llm.sh create mode 100644 __cc-haha-op.sh create mode 100644 zbak_script/linux/commit/___git_commit_push_llm.sh create mode 100644 zbak_script/win/commit/___git_commit_push_llm.ps1 diff --git a/.env_example_gitcommit b/.env_example_gitcommit new file mode 100644 index 000000000..e94af7bf5 --- /dev/null +++ b/.env_example_gitcommit @@ -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 \ No newline at end of file diff --git a/___git_commit_push_llm.sh b/___git_commit_push_llm.sh new file mode 100644 index 000000000..f82b41b56 --- /dev/null +++ b/___git_commit_push_llm.sh @@ -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 diff --git a/__cc-haha-op.sh b/__cc-haha-op.sh new file mode 100644 index 000000000..c9b239e8f --- /dev/null +++ b/__cc-haha-op.sh @@ -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 "$@" diff --git a/adapters/feishu/extract-payload.ts b/adapters/feishu/extract-payload.ts index 440359f1f..b6b35935d 100644 --- a/adapters/feishu/extract-payload.ts +++ b/adapters/feishu/extract-payload.ts @@ -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[] = [] diff --git a/adapters/feishu/index.ts b/adapters/feishu/index.ts index 9fa535e9b..966679008 100644 --- a/adapters/feishu/index.ts +++ b/adapters/feishu/index.ts @@ -952,6 +952,7 @@ function stripMentions(text: string): string { // ---------- event handlers ---------- async function handleMessage(data: any): Promise { + const event = data as { sender?: { sender_id?: { open_id?: string } } message?: { diff --git a/zbak_script/linux/commit/___git_commit_push_llm.sh b/zbak_script/linux/commit/___git_commit_push_llm.sh new file mode 100644 index 000000000..55f0769a7 --- /dev/null +++ b/zbak_script/linux/commit/___git_commit_push_llm.sh @@ -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 and tags (including the tags) + commitMessage=$(echo "$commitMessage" | sed -E '//,/<\/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!"}' diff --git a/zbak_script/win/commit/___git_commit_push_llm.ps1 b/zbak_script/win/commit/___git_commit_push_llm.ps1 new file mode 100644 index 000000000..8d524e85f --- /dev/null +++ b/zbak_script/win/commit/___git_commit_push_llm.ps1 @@ -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 , (, ( +- 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 ( and) + $commitMessage = $commitMessage -replace '(?s).*?', '' + $commitMessage = $commitMessage -replace '(?s).*?', '' + $commitMessage = $commitMessage -replace '(?s).*?', '' + $commitMessage = $commitMessage -replace '(?s).*?', '' + + # 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 '(? $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('"', '\"') + '"}') +} \ No newline at end of file