Merge 4256e696da7c64418e557275234d264aae61b3b1 into d1e684c419b4f88fee62490b0c2b4fcf65469e59

This commit is contained in:
burnlife 2026-07-12 07:13:14 +08:00 committed by GitHub
commit 5cb3b07ffb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 918 additions and 1 deletions

35
.env_example_gitcommit Normal file
View 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
View 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
View 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 "$@"

View File

@ -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[] = []

View File

@ -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?: {

View 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!"}'

View 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('"', '\"') + '"}')
}