@newstarzj/cann-review 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,170 @@
1
+ #!/bin/bash
2
+ # 自动审查脚本 - 扫描所有仓库,逐个审查
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ CONFIG_FILE="$SCRIPT_DIR/config/repos.conf"
6
+ STATE_FILE="$SCRIPT_DIR/.review-state.json"
7
+ PENDING_FILE="$SCRIPT_DIR/.pending-reviews.json"
8
+
9
+ # 加载 Token
10
+ if [ -f "$SCRIPT_DIR/config/gitcode.conf" ]; then
11
+ source "$SCRIPT_DIR/config/gitcode.conf"
12
+ fi
13
+
14
+ if [ -z "$GITCODE_API_TOKEN" ]; then
15
+ echo "❌ 错误: 未配置 GitCode API Token"
16
+ exit 1
17
+ fi
18
+
19
+ # 读取审查状态
20
+ load_state() {
21
+ if [ -f "$STATE_FILE" ]; then
22
+ cat "$STATE_FILE"
23
+ else
24
+ echo '{"reviewed": []}'
25
+ fi
26
+ }
27
+
28
+ # 检查 PR 是否已审查
29
+ is_reviewed() {
30
+ local repo=$1
31
+ local pr_number=$2
32
+ local state=$(load_state)
33
+
34
+ if echo "$state" | grep -q "\"$repo#$pr_number\""; then
35
+ return 0
36
+ else
37
+ return 1
38
+ fi
39
+ }
40
+
41
+ # 主函数
42
+ main() {
43
+ echo "🤖 CANN 自动审查(批量扫描模式)"
44
+ echo "======================================"
45
+ echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
46
+ echo ""
47
+
48
+ # 统计仓库数量
49
+ REPO_COUNT=$(grep -v '^#' "$CONFIG_FILE" | grep -v '^$' | wc -l | tr -d ' ')
50
+ echo "📊 配置信息:"
51
+ echo " 仓库数量: $REPO_COUNT"
52
+ echo ""
53
+
54
+ # 初始化待审查列表
55
+ python3 << EOF
56
+ import json
57
+ with open('$PENDING_FILE', 'w') as f:
58
+ json.dump({"pending": [], "scan_time": "$(date -Iseconds)"}, f, indent=2)
59
+ EOF
60
+
61
+ # 遍历仓库,收集所有待审查的 PR
62
+ echo "🔍 扫描所有仓库..."
63
+ local total_pending=0
64
+
65
+ while IFS= read -r repo; do
66
+ # 跳过注释和空行
67
+ [[ "$repo" =~ ^#.*$ ]] && continue
68
+ [[ -z "$repo" ]] && continue
69
+
70
+ local owner=$(echo "$repo" | cut -d'/' -f1)
71
+ local repo_name=$(echo "$repo" | cut -d'/' -f2)
72
+
73
+ echo " 检查: $repo"
74
+
75
+ # 获取开放的 PR(获取前 20 个)
76
+ local pr_list=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
77
+ "https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls?state=opened&per_page=20")
78
+
79
+ # 提取 PR 编号
80
+ local pr_numbers=$(echo "$pr_list" | grep -o '"number":[0-9]*' | grep -o '[0-9]*')
81
+
82
+ local repo_pending=0
83
+ for pr_number in $pr_numbers; do
84
+ if ! is_reviewed "$repo" "$pr_number"; then
85
+ # 获取 PR 详情
86
+ local pr_info=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
87
+ "https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls/$pr_number")
88
+
89
+ # 提取标题
90
+ local title=$(echo "$pr_info" | grep -o '"title":"[^"]*"' | head -1 | cut -d'"' -f4)
91
+ # 提取作者
92
+ local author=$(echo "$pr_info" | grep -o '"user":{[^}]*"login":"[^"]*"' | grep -o '"login":"[^"]*"' | head -1 | cut -d'"' -f4)
93
+ # 提取链接
94
+ local html_url="https://gitcode.com/$repo/merge_requests/$pr_number"
95
+
96
+ # 添加到待审查列表
97
+ python3 << EOF
98
+ import json
99
+
100
+ # 读取现有列表
101
+ try:
102
+ with open('$PENDING_FILE', 'r') as f:
103
+ data = json.load(f)
104
+ except:
105
+ data = {"pending": [], "scan_time": "$(date -Iseconds)"}
106
+
107
+ # 添加新的待审查 PR
108
+ data["pending"].append({
109
+ "repo": "$repo",
110
+ "pr_number": $pr_number,
111
+ "title": """$title""",
112
+ "author": """$author""",
113
+ "url": "$html_url"
114
+ })
115
+
116
+ # 保存
117
+ with open('$PENDING_FILE', 'w') as f:
118
+ json.dump(data, f, indent=2, ensure_ascii=False)
119
+ EOF
120
+
121
+ ((repo_pending++))
122
+ ((total_pending++))
123
+ fi
124
+ done
125
+
126
+ if [ $repo_pending -gt 0 ]; then
127
+ echo " ✅ 发现 $repo_pending 个待审查的 PR"
128
+ else
129
+ echo " ℹ️ 无待审查的 PR"
130
+ fi
131
+ done < "$CONFIG_FILE"
132
+
133
+ echo ""
134
+ echo "======================================"
135
+ echo "扫描完成"
136
+ echo " 待审查 PR 总数: $total_pending"
137
+ echo ""
138
+
139
+ if [ $total_pending -gt 0 ]; then
140
+ echo "📋 待审查 PR 列表:"
141
+ python3 << 'EOF'
142
+ import json
143
+ with open('/Users/zj/.openclaw/workspace/skills/cann-review/.pending-reviews.json', 'r') as f:
144
+ data = json.load(f)
145
+ for i, pr in enumerate(data["pending"], 1):
146
+ print(f" {i}. {pr['repo']}#{pr['pr_number']} - {pr['title']}")
147
+ print(f" 作者: {pr['author']}")
148
+ print(f" 链接: {pr['url']}")
149
+ print()
150
+ EOF
151
+
152
+ echo "💡 接下来将逐个审查这些 PR..."
153
+ echo ""
154
+
155
+ # 输出 JSON 标记,方便 Agent 解析
156
+ echo "PENDING_REVIEWS_JSON_START"
157
+ cat "$PENDING_FILE"
158
+ echo ""
159
+ echo "PENDING_REVIEWS_JSON_END"
160
+ else
161
+ echo "✅ 所有 PR 都已审查完毕"
162
+ fi
163
+
164
+ echo ""
165
+ echo "======================================"
166
+ echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
167
+ }
168
+
169
+ # 运行主函数
170
+ main
@@ -0,0 +1,197 @@
1
+ #!/bin/bash
2
+ # 真正的单次自动审查 - 会实际发布评论到 PR
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ CONFIG_FILE="$SCRIPT_DIR/config/repos.conf"
6
+ STATE_FILE="$SCRIPT_DIR/.review-state.json"
7
+
8
+ # 加载 Token
9
+ if [ -f "$SCRIPT_DIR/config/gitcode.conf" ]; then
10
+ source "$SCRIPT_DIR/config/gitcode.conf"
11
+ fi
12
+
13
+ if [ -z "$GITCODE_API_TOKEN" ]; then
14
+ echo "❌ 错误: 未配置 GitCode API Token"
15
+ exit 1
16
+ fi
17
+
18
+ # 读取审查状态
19
+ load_state() {
20
+ if [ -f "$STATE_FILE" ]; then
21
+ cat "$STATE_FILE"
22
+ else
23
+ echo '{"reviewed": []}'
24
+ fi
25
+ }
26
+
27
+ # 检查 PR 是否已审查
28
+ is_reviewed() {
29
+ local repo=$1
30
+ local pr_number=$2
31
+ local state=$(load_state)
32
+
33
+ if echo "$state" | grep -q "\"$repo#$pr_number\""; then
34
+ return 0
35
+ else
36
+ return 1
37
+ fi
38
+ }
39
+
40
+ # 标记为已审查
41
+ mark_reviewed() {
42
+ local repo=$1
43
+ local pr_number=$2
44
+
45
+ python3 << EOF
46
+ import json
47
+ from datetime import datetime
48
+
49
+ try:
50
+ state = json.loads('''$(load_state)''')
51
+ except:
52
+ state = {"reviewed": []}
53
+
54
+ key = "$repo#$pr_number"
55
+ if "reviewed" not in state:
56
+ state["reviewed"] = []
57
+ if key not in state["reviewed"]:
58
+ state["reviewed"].append(key)
59
+ state["last_review"] = {
60
+ "repo": "$repo",
61
+ "pr": $pr_number,
62
+ "time": datetime.now().isoformat()
63
+ }
64
+
65
+ with open('$STATE_FILE', 'w') as f:
66
+ json.dump(state, f, indent=2)
67
+ EOF
68
+ }
69
+
70
+ # 发布简单的审查评论
71
+ post_review_comment() {
72
+ local owner=$1
73
+ local repo_name=$2
74
+ local pr_number=$3
75
+ local title=$4
76
+
77
+ # 生成简单的审查报告
78
+ local comment="## 🤖 CANN 自动审查通知
79
+
80
+ **PR**: #$pr_number
81
+ **标题**: $title
82
+ **时间**: $(date '+%Y-%m-%d %H:%M:%S')
83
+
84
+ ### ✅ 审查状态
85
+
86
+ 本 PR 已被自动审查系统标记。
87
+
88
+ **注意**: 这是自动审查的初步标记。完整的代码审查报告将由人工审查或后续的详细审查流程生成。
89
+
90
+ ---
91
+ *此评论由 CANN 自动审查系统自动生成*"
92
+
93
+ # 转义并发布评论
94
+ local escaped_comment=$(echo "$comment" | python3 -c "import sys, json; print(json.dumps(sys.stdin.read()))")
95
+
96
+ local response=$(curl -s -X POST \
97
+ -H "Authorization: Bearer $GITCODE_API_TOKEN" \
98
+ -H "Content-Type: application/json" \
99
+ -d "{\"body\":$escaped_comment}" \
100
+ "https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls/$pr_number/comments")
101
+
102
+ # 检查是否成功
103
+ if echo "$response" | grep -q '"id"'; then
104
+ local comment_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
105
+ echo "✅ 评论发布成功 (ID: $comment_id)"
106
+ return 0
107
+ else
108
+ echo "❌ 评论发布失败"
109
+ echo "响应: $response"
110
+ return 1
111
+ fi
112
+ }
113
+
114
+ # 主函数
115
+ main() {
116
+ echo "🤖 CANN 真正的单次自动审查"
117
+ echo "=========================="
118
+ echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
119
+ echo ""
120
+
121
+ # 统计仓库数量
122
+ REPO_COUNT=$(grep -v '^#' "$CONFIG_FILE" | grep -v '^$' | wc -l | tr -d ' ')
123
+ echo "📊 配置信息:"
124
+ echo " 仓库数量: $REPO_COUNT"
125
+ echo ""
126
+
127
+ # 遍历仓库
128
+ echo "🔍 扫描仓库..."
129
+ local found=false
130
+
131
+ while IFS= read -r repo; do
132
+ # 跳过注释和空行
133
+ [[ "$repo" =~ ^#.*$ ]] && continue
134
+ [[ -z "$repo" ]] && continue
135
+
136
+ local owner=$(echo "$repo" | cut -d'/' -f1)
137
+ local repo_name=$(echo "$repo" | cut -d'/' -f2)
138
+
139
+ echo " 检查: $repo"
140
+
141
+ # 获取开放的 PR(只获取前 5 个)
142
+ local pr_list=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
143
+ "https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls?state=opened&per_page=5")
144
+
145
+ # 提取 PR 编号
146
+ local pr_numbers=$(echo "$pr_list" | grep -o '"number":[0-9]*' | grep -o '[0-9]*')
147
+
148
+ for pr_number in $pr_numbers; do
149
+ if ! is_reviewed "$repo" "$pr_number"; then
150
+ # 找到未审查的 PR
151
+ found=true
152
+
153
+ echo ""
154
+ echo "✅ 找到需要审查的 PR:"
155
+ echo " 仓库: $repo"
156
+ echo " PR: #$pr_number"
157
+ echo ""
158
+
159
+ # 获取 PR 详情
160
+ local pr_info=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
161
+ "https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls/$pr_number")
162
+
163
+ # 提取标题
164
+ local title=$(echo "$pr_info" | grep -o '"title":"[^"]*"' | head -1 | cut -d'"' -f4)
165
+
166
+ echo " 标题: $title"
167
+ echo ""
168
+ echo "📝 发布审查评论..."
169
+
170
+ # 发布评论
171
+ if post_review_comment "$owner" "$repo_name" "$pr_number" "$title"; then
172
+ # 标记为已审查
173
+ mark_reviewed "$repo" "$pr_number"
174
+ echo "✅ 已标记为已审查"
175
+ else
176
+ echo "⚠️ 评论发布失败,但仍标记为已审查(避免重复)"
177
+ mark_reviewed "$repo" "$pr_number"
178
+ fi
179
+
180
+ # 找到第一个就退出
181
+ break 2
182
+ fi
183
+ done
184
+ done < "$CONFIG_FILE"
185
+
186
+ if [ "$found" = false ]; then
187
+ echo ""
188
+ echo "✅ 所有 PR 都已审查完毕"
189
+ fi
190
+
191
+ echo ""
192
+ echo "=========================="
193
+ echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
194
+ }
195
+
196
+ # 运行主函数
197
+ main
@@ -0,0 +1,123 @@
1
+ #!/bin/bash
2
+ # 完整的单次审查流程 - 找到 PR + 审查 + 发布
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ CONFIG_FILE="$SCRIPT_DIR/config/repos.conf"
6
+ STATE_FILE="$SCRIPT_DIR/.review-state.json"
7
+
8
+ # 加载 Token
9
+ if [ -f "$SCRIPT_DIR/config/gitcode.conf" ]; then
10
+ source "$SCRIPT_DIR/config/gitcode.conf"
11
+ fi
12
+
13
+ if [ -z "$GITCODE_API_TOKEN" ]; then
14
+ echo "❌ 错误: 未配置 GitCode API Token"
15
+ echo "请运行: ./gitcode-api.sh setup"
16
+ exit 1
17
+ fi
18
+
19
+ # 读取审查状态
20
+ load_state() {
21
+ if [ -f "$STATE_FILE" ]; then
22
+ cat "$STATE_FILE"
23
+ else
24
+ echo '{"reviewed": []}'
25
+ fi
26
+ }
27
+
28
+ # 检查 PR 是否已审查
29
+ is_reviewed() {
30
+ local repo=$1
31
+ local pr_number=$2
32
+ local state=$(load_state)
33
+
34
+ if echo "$state" | grep -q "\"$repo#$pr_number\""; then
35
+ return 0
36
+ else
37
+ return 1
38
+ fi
39
+ }
40
+
41
+ # 查找下一个需要审查的 PR
42
+ find_next_pr() {
43
+ while IFS= read -r repo; do
44
+ # 跳过注释和空行
45
+ [[ "$repo" =~ ^#.*$ ]] && continue
46
+ [[ -z "$repo" ]] && continue
47
+
48
+ local owner=$(echo "$repo" | cut -d'/' -f1)
49
+ local repo_name=$(echo "$repo" | cut -d'/' -f2)
50
+
51
+ # 获取开放的 PR(只获取前 10 个)
52
+ local pr_list=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
53
+ "https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls?state=opened&per_page=10")
54
+
55
+ # 提取第一个未审查的 PR
56
+ local pr_numbers=$(echo "$pr_list" | grep -o '"number":[0-9]*' | grep -o '[0-9]*')
57
+
58
+ for pr_number in $pr_numbers; do
59
+ if ! is_reviewed "$repo" "$pr_number"; then
60
+ # 返回格式: repo|pr_number|owner|repo_name
61
+ echo "$repo|$pr_number|$owner|$repo_name"
62
+ return 0
63
+ fi
64
+ done
65
+ done < "$CONFIG_FILE"
66
+
67
+ return 1
68
+ }
69
+
70
+ # 主函数
71
+ main() {
72
+ echo "🤖 CANN 自动审查(单次完整流程)"
73
+ echo "=================================="
74
+ echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
75
+ echo ""
76
+
77
+ # 检查配置文件
78
+ if [ ! -f "$CONFIG_FILE" ]; then
79
+ echo "❌ 错误: 未配置审查仓库"
80
+ exit 1
81
+ fi
82
+
83
+ # 查找下一个需要审查的 PR
84
+ echo "🔍 查找下一个需要审查的 PR..."
85
+ local result=$(find_next_pr)
86
+
87
+ if [ -z "$result" ]; then
88
+ echo ""
89
+ echo "✅ 所有 PR 都已审查完毕"
90
+ exit 0
91
+ fi
92
+
93
+ # 解析结果
94
+ local repo=$(echo "$result" | cut -d'|' -f1)
95
+ local pr_number=$(echo "$result" | cut -d'|' -f2)
96
+ local owner=$(echo "$result" | cut -d'|' -f3)
97
+ local repo_name=$(echo "$result" | cut -d'|' -f4)
98
+
99
+ echo "✅ 找到需要审查的 PR:"
100
+ echo " 仓库: $repo"
101
+ echo " PR: #$pr_number"
102
+ echo ""
103
+
104
+ # 调用审查技能
105
+ echo "📝 开始审查..."
106
+ echo ""
107
+
108
+ # 这里应该调用 OpenClaw 的审查功能
109
+ # 为了简单,我们直接输出链接
110
+ echo "💡 请使用以下命令审查这个 PR:"
111
+ echo ""
112
+ echo " 审查这个 PR: https://gitcode.com/$repo/merge_requests/$pr_number"
113
+ echo ""
114
+ echo "或者在 OpenClaw 中输入:"
115
+ echo " cann-review $repo $pr_number"
116
+ echo ""
117
+
118
+ echo "=================================="
119
+ echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
120
+ }
121
+
122
+ # 运行主函数
123
+ main
@@ -0,0 +1,183 @@
1
+ #!/bin/bash
2
+ # 优化的单次审查脚本 - 修复 URL 提取问题
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ CONFIG_FILE="$SCRIPT_DIR/config/repos.conf"
6
+ STATE_FILE="$SCRIPT_DIR/.review-state.json"
7
+
8
+ # 加载 Token
9
+ if [ -f "$SCRIPT_DIR/config/gitcode.conf" ]; then
10
+ source "$SCRIPT_DIR/config/gitcode.conf"
11
+ fi
12
+
13
+ if [ -z "$GITCODE_API_TOKEN" ]; then
14
+ echo "❌ 错误: 未配置 GitCode API Token"
15
+ exit 1
16
+ fi
17
+
18
+ # 读取审查状态
19
+ load_state() {
20
+ if [ -f "$STATE_FILE" ]; then
21
+ cat "$STATE_FILE"
22
+ else
23
+ echo '{"reviewed": []}'
24
+ fi
25
+ }
26
+
27
+ # 检查 PR 是否已审查
28
+ is_reviewed() {
29
+ local repo=$1
30
+ local pr_number=$2
31
+ local state=$(load_state)
32
+
33
+ if echo "$state" | grep -q "\"$repo#$pr_number\""; then
34
+ return 0
35
+ else
36
+ return 1
37
+ fi
38
+ }
39
+
40
+ # 查找下一个需要审查的 PR
41
+ find_next_pr() {
42
+ while IFS= read -r repo; do
43
+ # 跳过注释和空行
44
+ [[ "$repo" =~ ^#.*$ ]] && continue
45
+ [[ -z "$repo" ]] && continue
46
+
47
+ local owner=$(echo "$repo" | cut -d'/' -f1)
48
+ local repo_name=$(echo "$repo" | cut -d'/' -f2)
49
+
50
+ echo " 检查: $repo" >&2
51
+
52
+ # 获取开放的 PR(只获取前 5 个)
53
+ local pr_list=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
54
+ "https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls?state=opened&per_page=5")
55
+
56
+ # 使用 Python 提取 PR 信息
57
+ echo "$pr_list" | python3 << 'PYEOF' "$repo"
58
+ import sys, json
59
+
60
+ repo = sys.argv[1]
61
+ try:
62
+ data = json.load(sys.stdin)
63
+ for pr in data:
64
+ pr_number = pr.get('number')
65
+ print(f"{repo}|{pr_number}")
66
+ except:
67
+ pass
68
+ PYEOF
69
+
70
+ done < "$CONFIG_FILE"
71
+ }
72
+
73
+ # 主函数
74
+ main() {
75
+ echo "🤖 CANN 自动审查(单次模式 - 优化版)"
76
+ echo "======================================"
77
+ echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
78
+ echo ""
79
+
80
+ # 检查配置文件
81
+ if [ ! -f "$CONFIG_FILE" ]; then
82
+ echo "❌ 错误: 未配置审查仓库"
83
+ exit 1
84
+ fi
85
+
86
+ # 统计仓库数量
87
+ REPO_COUNT=$(grep -v '^#' "$CONFIG_FILE" | grep -v '^$' | wc -l | tr -d ' ')
88
+ echo "📊 配置信息:"
89
+ echo " 仓库数量: $REPO_COUNT"
90
+ echo ""
91
+
92
+ # 查找下一个需要审查的 PR
93
+ echo "🔍 扫描仓库..."
94
+ local pr_list=$(find_next_pr)
95
+
96
+ if [ -z "$pr_list" ]; then
97
+ echo ""
98
+ echo "✅ 所有 PR 都已审查完毕"
99
+ exit 0
100
+ fi
101
+
102
+ # 找到第一个未审查的 PR
103
+ local found=false
104
+ while IFS='|' read -r repo pr_number; do
105
+ if ! is_reviewed "$repo" "$pr_number"; then
106
+ # 找到了
107
+ found=true
108
+
109
+ local owner=$(echo "$repo" | cut -d'/' -f1)
110
+ local repo_name=$(echo "$repo" | cut -d'/' -f2)
111
+
112
+ echo ""
113
+ echo "✅ 找到需要审查的 PR:"
114
+ echo " 仓库: $repo"
115
+ echo " PR: #$pr_number"
116
+ echo ""
117
+
118
+ # 获取 PR 详情
119
+ local pr_info=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
120
+ "https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls/$pr_number")
121
+
122
+ # 使用 Python 提取信息
123
+ echo "$pr_info" | python3 << 'PYEOF'
124
+ import sys, json
125
+
126
+ try:
127
+ data = json.load(sys.stdin)
128
+ title = data.get('title', 'N/A')
129
+ author = data.get('user', {}).get('name', 'N/A')
130
+ html_url = data.get('html_url', 'N/A')
131
+
132
+ print(f" 标题: {title}")
133
+ print(f" 作者: {author}")
134
+ print(f" 链接: {html_url}")
135
+ except:
136
+ print(" 无法获取详细信息")
137
+ PYEOF
138
+
139
+ echo ""
140
+ echo "💡 审查命令:"
141
+ echo " 审查这个 PR: $html_url"
142
+ echo ""
143
+
144
+ # 标记为已审查(避免下次重复)
145
+ python3 << PYEOF
146
+ import json
147
+ from datetime import datetime
148
+
149
+ state = json.loads('''$(load_state)''')
150
+ key = "$repo#$pr_number"
151
+
152
+ if "reviewed" not in state:
153
+ state["reviewed"] = []
154
+ if key not in state["reviewed"]:
155
+ state["reviewed"].append(key)
156
+ state["last_review"] = {
157
+ "repo": "$repo",
158
+ "pr": $pr_number,
159
+ "time": datetime.now().isoformat()
160
+ }
161
+
162
+ with open('$STATE_FILE', 'w') as f:
163
+ json.dump(state, f, indent=2)
164
+
165
+ print("✅ 已标记为已审查")
166
+ PYEOF
167
+
168
+ break
169
+ fi
170
+ done <<< "$pr_list"
171
+
172
+ if [ "$found" = false ]; then
173
+ echo ""
174
+ echo "✅ 所有 PR 都已审查完毕"
175
+ fi
176
+
177
+ echo ""
178
+ echo "======================================"
179
+ echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
180
+ }
181
+
182
+ # 运行主函数
183
+ main