- 新增 custom_reviewer_prompt 和 custom_commit_msg_prompt,兼容旧 custom_prompt - pre-commit hook 安装时备份已有 hook,卸载时恢复 - 支持 --format json 输出评审结果 - diff 发送前自动脱敏敏感信息
74 lines
2.1 KiB
Go
74 lines
2.1 KiB
Go
package reviewer
|
||
|
||
import (
|
||
"regexp"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
type Finding struct {
|
||
Severity string `json:"severity"`
|
||
Location string `json:"location,omitempty"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
type ReviewReport struct {
|
||
Format string `json:"format"`
|
||
Verdict string `json:"verdict,omitempty"`
|
||
Score int `json:"score,omitempty"`
|
||
Findings []Finding `json:"findings"`
|
||
Raw string `json:"raw"`
|
||
}
|
||
|
||
var (
|
||
verdictPattern = regexp.MustCompile(`(?mi)(?:结论|verdict)\s*[::]\s*\[?(PASS|WARN|WARNING|REJECT|BLOCKER|CRITICAL)\]?`)
|
||
scorePattern = regexp.MustCompile(`(?mi)(?:评分|score)\s*[::]\s*(\d{1,3})`)
|
||
findingPattern = regexp.MustCompile(`(?m)^\s*-\s*\[(BLOCKER|CRITICAL|WARNING|WARN|SUGGESTION|INFO)\]\s+(.+?)\s*$`)
|
||
)
|
||
|
||
// ParseReviewReport 将模型的 Markdown Review 转成稳定的 JSON 结构。
|
||
// Raw 始终保留完整原文,解析失败时下游仍可自行处理。
|
||
func ParseReviewReport(raw string) ReviewReport {
|
||
report := ReviewReport{
|
||
Format: "git-air-review/v1",
|
||
Findings: []Finding{},
|
||
Raw: strings.TrimSpace(raw),
|
||
}
|
||
|
||
if match := verdictPattern.FindStringSubmatch(raw); len(match) > 1 {
|
||
report.Verdict = strings.ToUpper(match[1])
|
||
}
|
||
if match := scorePattern.FindStringSubmatch(raw); len(match) > 1 {
|
||
report.Score, _ = strconv.Atoi(match[1])
|
||
if report.Score > 100 {
|
||
report.Score = 100
|
||
}
|
||
}
|
||
|
||
for _, match := range findingPattern.FindAllStringSubmatch(raw, -1) {
|
||
location, message := splitFindingContent(match[2])
|
||
report.Findings = append(report.Findings, Finding{
|
||
Severity: strings.ToUpper(match[1]),
|
||
Location: location,
|
||
Message: message,
|
||
})
|
||
}
|
||
return report
|
||
}
|
||
|
||
func splitFindingContent(content string) (string, string) {
|
||
content = strings.TrimSpace(content)
|
||
parts := strings.SplitN(content, " - ", 2)
|
||
if len(parts) == 1 {
|
||
return "", content
|
||
}
|
||
|
||
location := strings.TrimSpace(parts[0])
|
||
message := strings.TrimSpace(parts[1])
|
||
// 没有行号/路径特征时,不要误把普通句子当成 location。
|
||
if !strings.Contains(location, ":") {
|
||
return "", content
|
||
}
|
||
return location, message
|
||
}
|