Loading...
Loading...
Compare original and translation side by side
<label> [decorations]: <subject>
[discussion]<label> [修饰符]: <主题>
[讨论内容]| Label | Meaning | Blocks Merge? |
|---|---|---|
| praise | Highlight something positive | No |
| nitpick | Minor, optional suggestion | No |
| suggestion | Propose an improvement | No |
| issue | Problem that should be addressed | Usually |
| question | Request clarification | No |
| thought | Idea to consider | No |
| chore | Routine task (formatting, deps) | No |
| note | Informational comment | No |
| todo | Follow-up work needed | Maybe |
| security | Security concern | Yes |
| bug | Potential bug | Yes |
| breaking | Breaking change | Yes |
| 标签 | 含义 | 是否阻止合并? |
|---|---|---|
| praise | 突出优秀的实现 | 否 |
| nitpick | 微小的可选建议 | 否 |
| suggestion | 提出改进方案 | 否 |
| issue | 需要解决的问题 | 通常是 |
| question | 请求澄清 | 否 |
| thought | 供参考的想法 | 否 |
| chore | 常规任务(格式、依赖) | 否 |
| note | 信息性说明 | 否 |
| todo | 需要后续跟进的工作 | 可能 |
| security | 安全问题 | 是 |
| bug | 潜在bug | 是 |
| breaking | 破坏性变更 | 是 |
| Decoration | Meaning |
|---|---|
| [blocking] | Must be addressed before merge |
| [non-blocking] | Optional, can be deferred |
| [if-minor] | Only if it's a quick fix |
| 修饰符 | 含义 |
|---|---|
| [blocking] | 合并前必须解决 |
| [non-blocking] | 可选,可推迟处理 |
| [if-minor] | 仅当修改成本极低时才需要处理 |
// ✅ Good: Clear, specific, actionable
praise: Excellent use of TypeScript generics here!
This makes the function much more reusable while maintaining type safety.
---
nitpick [non-blocking]: Consider using const instead of let
This variable is never reassigned, so `const` would be more appropriate:
```typescript
const MAX_RETRIES = 3;try {
const data = await fetchUser(userId);
// ...
} catch (error) {
logger.error('Failed to fetch user', { userId, error });
throw new UserNotFoundError(userId);
}/api/admin/usersrouter.get('/api/admin/users', requireAdmin, getUsers);const CACHE_TTL_SECONDS = 3600;
cache.set(key, value, CACHE_TTL_SECONDS);
---// ✅ 优秀示例:清晰、具体、可执行
praise: 这里TypeScript泛型的使用非常出色!
这让函数在保持类型安全的同时大幅提升了复用性。
---
nitpick [non-blocking]: 考虑使用const替代let
该变量从未被重新赋值,因此使用`const`更合适:
```typescript
const MAX_RETRIES = 3;try {
const data = await fetchUser(userId);
// ...
} catch (error) {
logger.error('获取用户失败', { userId, error });
throw new UserNotFoundError(userId);
}/api/admin/usersrouter.get('/api/admin/users', requireAdmin, getUsers);const CACHE_TTL_SECONDS = 3600;
cache.set(key, value, CACHE_TTL_SECONDS);
---any?.any?.withexcept:withexcept:issue: This function doesn't handle the case where the user is null
If `getUserById()` returns null (user not found), this will throw:
`Cannot read property 'email' of null`
Add a null check:
```typescript
const user = await getUserById(userId);
if (!user) {
throw new UserNotFoundError(userId);
}
return user.email;
**Why it's good:**
- Specific (points to exact problem)
- Explains the impact (what will happen)
- Suggests a concrete solution
- Provides example codeissue: 这个函数未处理用户为null的情况
如果`getUserById()`返回null(用户不存在),会抛出错误:
`Cannot read property 'email' of null`
请添加null检查:
```typescript
const user = await getUserById(userId);
if (!user) {
throw new UserNotFoundError(userId);
}
return user.email;
**为什么优秀:**
- 具体(指出了确切问题)
- 说明了影响(会发生什么)
- 给出了明确的解决方案
- 提供了代码示例This is wrong. Fix it.这不对,修复它。✅ Good Response:
> Good catch! I didn't consider the null case.
> I've added the null check in commit abc123.
✅ Good Response (Disagreement):
> I chose a Map here because we need O(1) lookups on a large dataset.
> An object would work but performs worse at scale (n > 10,000).
> I added a comment explaining this tradeoff.
❌ Bad Response:
> This works fine. Not changing it.✅ 优秀回复:
> 好发现!我之前没考虑到null的情况。
> 我已经在提交abc123中添加了null检查。
✅ 优秀回复(有分歧):
> 我选择Map是因为我们需要对大数据集进行O(1)查找。
> 对象也能工作,但在数据量较大时(n > 10,000)性能更差。
> 我已经添加了注释说明这个权衡。
❌ 糟糕回复:
> 这个没问题,我不改。nitpick: Add a space here
nitpick: Rename this variable to `userInfo` instead of `userData`
nitpick: Move this import to the topThis should be a class instead of functions.nitpick: 这里加个空格
nitpick: 把变量名从`userData`改成`userInfo`
nitpick: 把这个导入移到顶部这应该用类而不是函数。undefinedundefinedundefinedundefinedundefinedundefined
---
---| Metric | Target | Purpose |
|---|---|---|
| Review Time | < 24 hours | Fast feedback loop |
| PR Size | < 400 lines | Manageable reviews |
| Approval Rate (first review) | 20-30% | Balance speed vs quality |
| Comments per PR | 3-10 | Engaged but not overwhelming |
| Back-and-Forth Rounds | 1-2 | Efficient communication |
| Time to Merge (after approval) | < 2 hours | Avoid stale branches |
| 指标 | 目标 | 目的 |
|---|---|---|
| 评审耗时 | <24小时 | 快速反馈循环 |
| PR规模 | <400行 | 便于评审 |
| 首次评审批准率 | 20-30% | 平衡速度与质量 |
| 每个PR的评论数 | 3-10 | 充分参与但不过度 |
| 往返次数 | 1-2 | 高效沟通 |
| 批准后合并耗时 | <2小时 | 避免分支过时 |
import Anthropic from '@anthropic-ai/sdk';
interface ReviewResult {
issues: ReviewIssue[];
patterns: DetectedPattern[];
recommendations: string[];
riskScore: number;
}
async function performDeepCodeReview(
prDiff: string,
codebaseContext: string
): Promise<ReviewResult> {
const anthropic = new Anthropic();
// Extended thinking requires budget_tokens < max_tokens
// Minimum budget: 1,024 tokens
const response = await anthropic.messages.create({
model: 'claude-opus-4-5-20251101',
max_tokens: 16000,
thinking: {
type: 'enabled',
budget_tokens: 8000 // Deep analysis for complex reviews
},
messages: [{
role: 'user',
content: `
Perform a comprehensive code review analyzing:
## PR Changes
${prDiff}
## Codebase Context
${codebaseContext}
## Analysis Requirements
1. **Systemic Pattern Detection**: Identify recurring patterns (good and bad)
2. **Cross-File Impact**: Trace how changes affect other modules
3. **Security Analysis**: Deep scan for vulnerabilities (OWASP Top 10)
4. **Architectural Consistency**: Verify alignment with existing patterns
5. **Technical Debt Assessment**: Identify debt being added or resolved
Provide structured output with:
- Critical issues (must fix)
- Warnings (should fix)
- Suggestions (nice to have)
- Detected patterns (positive and negative)
- Risk score (1-10)
`
}]
});
// Response contains thinking blocks followed by text blocks
// content: [{ type: 'thinking', thinking: '...' }, { type: 'text', text: '...' }]
return parseReviewResponse(response);
}import Anthropic from '@anthropic-ai/sdk';
interface ReviewResult {
issues: ReviewIssue[];
patterns: DetectedPattern[];
recommendations: string[];
riskScore: number;
}
async function performDeepCodeReview(
prDiff: string,
codebaseContext: string
): Promise<ReviewResult> {
const anthropic = new Anthropic();
// 深度思考要求budget_tokens < max_tokens
// 最小预算:1,024 tokens
const response = await anthropic.messages.create({
model: 'claude-opus-4-5-20251101',
max_tokens: 16000,
thinking: {
type: 'enabled',
budget_tokens: 8000 // 复杂评审的深度分析
},
messages: [{
role: 'user',
content: `
执行全面的代码评审,分析以下内容:
## PR变更
${prDiff}
## 代码库上下文
${codebaseContext}
## 分析要求
1. **系统性模式检测**:识别重复出现的模式(好的和坏的)
2. **跨文件影响**:追踪变更对其他模块的影响
3. **安全分析**:深度扫描漏洞(OWASP Top 10)
4. **架构一致性**:验证与现有模式的对齐度
5. **技术债务评估**:识别新增或解决的技术债务
提供结构化输出,包含:
- 关键问题(必须修复)
- 警告(应该修复)
- 建议(优化项)
- 检测到的模式(正面和负面)
- 风险评分(1-10)
`
}]
});
// 响应包含思考块和文本块
// content: [{ type: 'thinking', thinking: '...' }, { type: 'text', text: '...' }]
return parseReviewResponse(response);
}interface CodeSmellPattern {
type: 'duplication' | 'coupling' | 'complexity' | 'naming' | 'architecture';
severity: 'critical' | 'warning' | 'info';
locations: FileLocation[];
description: string;
suggestion: string;
}
const SMELL_PATTERNS = {
// Cross-file duplication
duplication: {
triggers: ['similar logic in 3+ files', 'copy-paste patterns', 'repeated error handling'],
action: 'Extract to shared utility or base class'
},
// Tight coupling
coupling: {
triggers: ['circular imports', 'god objects', 'feature envy'],
action: 'Apply dependency injection or interface segregation'
},
// Complexity creep
complexity: {
triggers: ['nested callbacks > 3 levels', 'functions > 50 lines', 'cyclomatic complexity > 10'],
action: 'Decompose into smaller, focused functions'
},
// Inconsistent naming
naming: {
triggers: ['mixed conventions', 'unclear abbreviations', 'inconsistent pluralization'],
action: 'Align with codebase naming conventions'
},
// Architectural drift
architecture: {
triggers: ['layer violations', 'missing abstractions', 'inappropriate intimacy'],
action: 'Refactor to follow established architectural patterns'
}
};interface CodeSmellPattern {
type: 'duplication' | 'coupling' | 'complexity' | 'naming' | 'architecture';
severity: 'critical' | 'warning' | 'info';
locations: FileLocation[];
description: string;
suggestion: string;
}
const SMELL_PATTERNS = {
// 跨文件重复
duplication: {
触发条件: ['3个以上文件有相似逻辑', '复制粘贴模式', '重复的错误处理'],
行动: '提取到共享工具类或基类'
},
// 紧耦合
coupling: {
触发条件: ['循环导入', '上帝对象', '特性羡慕'],
行动: '应用依赖注入或接口隔离'
},
// 复杂度攀升
complexity: {
触发条件: ['嵌套回调>3层', '函数>50行', '圈复杂度>10'],
行动: '分解为更小、聚焦的函数'
},
// 命名不一致
naming: {
触发条件: ['混合命名规范', '模糊的缩写', '复数形式不一致'],
行动: '与代码库命名规范对齐'
},
// 架构漂移
architecture: {
触发条件: ['层级违反', '缺少抽象', '不当亲密'],
行动: '重构以遵循既定架构模式'
}
};interface SecurityFinding {
category: 'injection' | 'auth' | 'crypto' | 'exposure' | 'config';
severity: 'critical' | 'high' | 'medium' | 'low';
cwe: string; // CWE ID
location: FileLocation;
description: string;
remediation: string;
}
async function performSecurityReview(
code: string,
context: SecurityContext
): Promise<SecurityFinding[]> {
const anthropic = new Anthropic();
const response = await anthropic.messages.create({
model: 'claude-opus-4-5-20251101',
max_tokens: 12000,
thinking: {
type: 'enabled',
budget_tokens: 6000 // Security analysis needs deep reasoning
},
messages: [{
role: 'user',
content: `
Perform security analysis on this code:
${code}
Context:
- Language: ${context.language}
- Framework: ${context.framework}
- Exposure: ${context.isPublicFacing ? 'Public' : 'Internal'}
Check for:
1. Injection vulnerabilities (SQL, XSS, command)
2. Authentication/Authorization flaws
3. Cryptographic weaknesses
4. Sensitive data exposure
5. Security misconfiguration
For each finding, provide CWE ID and specific remediation.
`
}]
});
return parseSecurityFindings(response);
}interface SecurityFinding {
category: 'injection' | 'auth' | 'crypto' | 'exposure' | 'config';
severity: 'critical' | 'high' | 'medium' | 'low';
cwe: string; // CWE编号
location: FileLocation;
description: string;
remediation: string;
}
async function performSecurityReview(
code: string,
context: SecurityContext
): Promise<SecurityFinding[]> {
const anthropic = new Anthropic();
const response = await anthropic.messages.create({
model: 'claude-opus-4-5-20251101',
max_tokens: 12000,
thinking: {
type: 'enabled',
budget_tokens: 6000 // 安全分析需要深度推理
},
messages: [{
role: 'user',
content: `
对以下代码进行安全分析:
${code}
上下文:
- 语言: ${context.language}
- 框架: ${context.framework}
- 暴露程度: ${context.isPublicFacing ? '公开' : '内部'}
检查内容:
1. 注入漏洞(SQL、XSS、命令注入)
2. 身份验证/授权缺陷
3. 加密弱点
4. 敏感数据暴露
5. 安全配置错误
每个发现请提供CWE编号和具体修复建议。
`
}]
});
return parseSecurityFindings(response);
}interface ImpactAnalysis {
directImpact: FileImpact[];
indirectImpact: FileImpact[];
breakingChanges: BreakingChange[];
testCoverage: TestCoverageGap[];
}
async function analyzeChangeImpact(
changedFiles: string[],
dependencyGraph: DependencyGraph
): Promise<ImpactAnalysis> {
// Build impact graph
const directlyAffected = new Set<string>();
const indirectlyAffected = new Set<string>();
for (const file of changedFiles) {
// Files that import this file
const dependents = dependencyGraph.getDependents(file);
dependents.forEach(d => directlyAffected.add(d));
// Second-level dependents
for (const dependent of dependents) {
const secondLevel = dependencyGraph.getDependents(dependent);
secondLevel.forEach(d => {
if (!directlyAffected.has(d)) {
indirectlyAffected.add(d);
}
});
}
}
// Use extended thinking for breaking change analysis
const breakingAnalysis = await analyzeBreakingChanges(
changedFiles,
Array.from(directlyAffected)
);
return {
directImpact: Array.from(directlyAffected).map(f => ({ file: f, type: 'direct' })),
indirectImpact: Array.from(indirectlyAffected).map(f => ({ file: f, type: 'indirect' })),
breakingChanges: breakingAnalysis.breaking,
testCoverage: breakingAnalysis.gaps
};
}interface ImpactAnalysis {
directImpact: FileImpact[];
indirectImpact: FileImpact[];
breakingChanges: BreakingChange[];
testCoverage: TestCoverageGap[];
}
async function analyzeChangeImpact(
changedFiles: string[],
dependencyGraph: DependencyGraph
): Promise<ImpactAnalysis> {
// 构建影响图
const directlyAffected = new Set<string>();
const indirectlyAffected = new Set<string>();
for (const file of changedFiles) {
// 导入了该文件的文件
const dependents = dependencyGraph.getDependents(file);
dependents.forEach(d => directlyAffected.add(d));
// 二级依赖文件
for (const dependent of dependents) {
const secondLevel = dependencyGraph.getDependents(dependent);
secondLevel.forEach(d => {
if (!directlyAffected.has(d)) {
indirectlyAffected.add(d);
}
});
}
}
// 使用深度思考分析破坏性变更
const breakingAnalysis = await analyzeBreakingChanges(
changedFiles,
Array.from(directlyAffected)
);
return {
directImpact: Array.from(directlyAffected).map(f => ({ file: f, type: 'direct' })),
indirectImpact: Array.from(indirectlyAffected).map(f => ({ file: f, type: 'indirect' })),
breakingChanges: breakingAnalysis.breaking,
testCoverage: breakingAnalysis.gaps
};
}type ModelTier = 'opus' | 'sonnet' | 'haiku';
interface ReviewConfig {
model: ModelTier;
thinkingBudget?: number;
focus: string[];
}
function selectReviewModel(pr: PullRequest): ReviewConfig {
// Critical path - use Opus with extended thinking
if (pr.touchesCriticalPath || pr.isSecurityRelated) {
return {
model: 'opus',
thinkingBudget: 8000,
focus: ['security', 'architecture', 'breaking-changes']
};
}
// Large PRs - use Opus for systemic analysis
if (pr.filesChanged > 10 || pr.linesChanged > 500) {
return {
model: 'opus',
thinkingBudget: 5000,
focus: ['patterns', 'impact', 'consistency']
};
}
// Standard PRs - Sonnet for thorough review
if (pr.linesChanged > 100) {
return {
model: 'sonnet',
focus: ['correctness', 'style', 'tests']
};
}
// Small changes - Haiku for quick review
return {
model: 'haiku',
focus: ['correctness', 'style']
};
}type ModelTier = 'opus' | 'sonnet' | 'haiku';
interface ReviewConfig {
model: ModelTier;
thinkingBudget?: number;
focus: string[];
}
function selectReviewModel(pr: PullRequest): ReviewConfig {
// 关键路径 - 使用Opus并开启深度思考
if (pr.touchesCriticalPath || pr.isSecurityRelated) {
return {
model: 'opus',
thinkingBudget: 8000,
focus: ['security', 'architecture', 'breaking-changes']
};
}
// 大型PR - 使用Opus进行系统性分析
if (pr.filesChanged > 10 || pr.linesChanged > 500) {
return {
model: 'opus',
thinkingBudget: 5000,
focus: ['patterns', 'impact', 'consistency']
};
}
// 标准PR - 使用Sonnet进行全面评审
if (pr.linesChanged > 100) {
return {
model: 'sonnet',
focus: ['correctness', 'style', 'tests']
};
}
// 小型变更 - 使用Haiku快速评审
return {
model: 'haiku',
focus: ['correctness', 'style']
};
}interface GeneratedComment {
label: 'praise' | 'nitpick' | 'suggestion' | 'issue' | 'question' | 'security' | 'bug';
decoration?: 'blocking' | 'non-blocking' | 'if-minor';
subject: string;
discussion: string;
location: { file: string; line: number };
}
function formatConventionalComment(comment: GeneratedComment): string {
const decoration = comment.decoration ? ` [${comment.decoration}]` : '';
return `${comment.label}${decoration}: ${comment.subject}\n\n${comment.discussion}`;
}
// Example output:
// security [blocking]: SQL injection vulnerability in user search
//
// The search query is constructed using string concatenation:
// ```typescript
// const query = `SELECT * FROM users WHERE name = '${searchTerm}'`;
// ```
//
// Use parameterized queries instead:
// ```typescript
// const query = 'SELECT * FROM users WHERE name = $1';
// const result = await db.query(query, [searchTerm]);
// ```interface GeneratedComment {
label: 'praise' | 'nitpick' | 'suggestion' | 'issue' | 'question' | 'security' | 'bug';
decoration?: 'blocking' | 'non-blocking' | 'if-minor';
subject: string;
discussion: string;
location: { file: string; line: number };
}
function formatConventionalComment(comment: GeneratedComment): string {
const decoration = comment.decoration ? ` [${comment.decoration}]` : '';
return `${comment.label}${decoration}: ${comment.subject}\n\n${comment.discussion}`;
}
// 示例输出:
// security [blocking]: 用户搜索存在SQL注入漏洞
//
// 查询语句使用字符串拼接构建:
// ```typescript
// const query = `SELECT * FROM users WHERE name = '${searchTerm}'`;
// ```
//
// 请改用参数化查询:
// ```typescript
// const query = 'SELECT * FROM users WHERE name = $1';
// const result = await db.query(query, [searchTerm]);
// ```