mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
feat: Add orchestrator and state management for code review process
- Implemented orchestrator logic to manage code review phases, including state reading, action selection, and execution loop. - Defined state schema for review process, including metadata, context, findings, and execution tracking. - Created action catalog detailing actions for context collection, quick scan, deep review, report generation, and completion. - Established error recovery strategies and termination conditions for robust review handling. - Developed issue classification and quality standards documentation to guide review severity and categorization. - Introduced review dimensions with detailed checklists for correctness, security, performance, readability, testing, and architecture. - Added templates for issue reporting and review reports to standardize output and improve clarity.
This commit is contained in:
170
.claude/skills/review-code/SKILL.md
Normal file
170
.claude/skills/review-code/SKILL.md
Normal file
@@ -0,0 +1,170 @@
|
||||
---
|
||||
name: review-code
|
||||
description: Multi-dimensional code review with structured reports. Analyzes correctness, readability, performance, security, testing, and architecture. Triggers on "review code", "code review", "审查代码", "代码审查".
|
||||
allowed-tools: Task, AskUserQuestion, Read, Write, Glob, Grep, Bash, mcp__ace-tool__search_context, mcp__ide__getDiagnostics
|
||||
---
|
||||
|
||||
# Review Code
|
||||
|
||||
Multi-dimensional code review skill that analyzes code across 6 key dimensions and generates structured review reports with actionable recommendations.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ⚠️ Phase 0: Specification Study (强制前置) │
|
||||
│ → 阅读 specs/review-dimensions.md │
|
||||
│ → 理解审查维度和问题分类标准 │
|
||||
└───────────────┬─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Orchestrator (状态驱动决策) │
|
||||
│ → 读取状态 → 选择审查动作 → 执行 → 更新状态 │
|
||||
└───────────────┬─────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────┼───────────┬───────────┬───────────┐
|
||||
↓ ↓ ↓ ↓ ↓
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ Collect │ │ Quick │ │ Deep │ │ Report │ │Complete │
|
||||
│ Context │ │ Scan │ │ Review │ │ Generate│ │ │
|
||||
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
|
||||
↓ ↓ ↓ ↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Review Dimensions │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │Correctness│ │Readability│ │Performance│ │ Security │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Testing │ │Architecture│ │
|
||||
│ └──────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **多维度审查**: 覆盖正确性、可读性、性能、安全性、测试覆盖、架构一致性六大维度
|
||||
2. **分层执行**: 快速扫描识别高风险区域,深入审查聚焦关键问题
|
||||
3. **结构化报告**: 按严重程度分类,提供文件位置和修复建议
|
||||
4. **状态驱动**: 自主模式,根据审查进度动态选择下一步动作
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Mandatory Prerequisites (强制前置条件)
|
||||
|
||||
> **⛔ 禁止跳过**: 在执行任何审查操作之前,**必须**完整阅读以下文档。
|
||||
|
||||
### 规范文档 (必读)
|
||||
|
||||
| Document | Purpose | Priority |
|
||||
|----------|---------|----------|
|
||||
| [specs/review-dimensions.md](specs/review-dimensions.md) | 审查维度定义和检查点 | **P0 - 最高** |
|
||||
| [specs/issue-classification.md](specs/issue-classification.md) | 问题分类和严重程度标准 | **P0 - 最高** |
|
||||
| [specs/quality-standards.md](specs/quality-standards.md) | 审查质量标准 | P1 |
|
||||
|
||||
### 模板文件 (生成前必读)
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| [templates/review-report.md](templates/review-report.md) | 审查报告模板 |
|
||||
| [templates/issue-template.md](templates/issue-template.md) | 问题记录模板 |
|
||||
|
||||
---
|
||||
|
||||
## Execution Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Phase 0: Specification Study (强制前置 - 禁止跳过) │
|
||||
│ → Read: specs/review-dimensions.md │
|
||||
│ → Read: specs/issue-classification.md │
|
||||
│ → 理解审查标准和问题分类 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Action: collect-context │
|
||||
│ → 收集目标文件/目录 │
|
||||
│ → 识别技术栈和语言 │
|
||||
│ → Output: state.context (files, language, framework) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Action: quick-scan │
|
||||
│ → 快速扫描整体结构 │
|
||||
│ → 识别高风险区域 │
|
||||
│ → Output: state.risk_areas, state.scan_summary │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Action: deep-review (per dimension) │
|
||||
│ → 逐维度深入审查 │
|
||||
│ → 记录发现的问题 │
|
||||
│ → Output: state.findings[] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Action: generate-report │
|
||||
│ → 汇总所有发现 │
|
||||
│ → 生成结构化报告 │
|
||||
│ → Output: review-report.md │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Action: complete │
|
||||
│ → 保存最终状态 │
|
||||
│ → 输出审查摘要 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Directory Setup
|
||||
|
||||
```javascript
|
||||
const timestamp = new Date().toISOString().slice(0,19).replace(/[-:T]/g, '');
|
||||
const workDir = `.workflow/.scratchpad/review-code-${timestamp}`;
|
||||
|
||||
Bash(`mkdir -p "${workDir}"`);
|
||||
Bash(`mkdir -p "${workDir}/findings"`);
|
||||
```
|
||||
|
||||
## Output Structure
|
||||
|
||||
```
|
||||
.workflow/.scratchpad/review-code-{timestamp}/
|
||||
├── state.json # 审查状态
|
||||
├── context.json # 目标上下文
|
||||
├── findings/ # 问题发现
|
||||
│ ├── correctness.json
|
||||
│ ├── readability.json
|
||||
│ ├── performance.json
|
||||
│ ├── security.json
|
||||
│ ├── testing.json
|
||||
│ └── architecture.json
|
||||
└── review-report.md # 最终审查报告
|
||||
```
|
||||
|
||||
## Review Dimensions
|
||||
|
||||
| Dimension | Focus Areas | Key Checks |
|
||||
|-----------|-------------|------------|
|
||||
| **Correctness** | 逻辑正确性 | 边界条件、错误处理、null 检查 |
|
||||
| **Readability** | 代码可读性 | 命名规范、函数长度、注释质量 |
|
||||
| **Performance** | 性能效率 | 算法复杂度、I/O 优化、资源使用 |
|
||||
| **Security** | 安全性 | 注入风险、敏感信息、权限控制 |
|
||||
| **Testing** | 测试覆盖 | 测试充分性、边界覆盖、可维护性 |
|
||||
| **Architecture** | 架构一致性 | 设计模式、分层结构、依赖管理 |
|
||||
|
||||
## Issue Severity Levels
|
||||
|
||||
| Level | Prefix | Description | Action Required |
|
||||
|-------|--------|-------------|-----------------|
|
||||
| **Critical** | [C] | 阻塞性问题,必须立即修复 | Must fix before merge |
|
||||
| **High** | [H] | 重要问题,需要修复 | Should fix |
|
||||
| **Medium** | [M] | 建议改进 | Consider fixing |
|
||||
| **Low** | [L] | 可选优化 | Nice to have |
|
||||
| **Info** | [I] | 信息性建议 | For reference |
|
||||
|
||||
## Reference Documents
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| [phases/orchestrator.md](phases/orchestrator.md) | 审查编排器 |
|
||||
| [phases/state-schema.md](phases/state-schema.md) | 状态结构定义 |
|
||||
| [phases/actions/action-collect-context.md](phases/actions/action-collect-context.md) | 收集上下文 |
|
||||
| [phases/actions/action-quick-scan.md](phases/actions/action-quick-scan.md) | 快速扫描 |
|
||||
| [phases/actions/action-deep-review.md](phases/actions/action-deep-review.md) | 深入审查 |
|
||||
| [phases/actions/action-generate-report.md](phases/actions/action-generate-report.md) | 生成报告 |
|
||||
| [phases/actions/action-complete.md](phases/actions/action-complete.md) | 完成审查 |
|
||||
| [specs/review-dimensions.md](specs/review-dimensions.md) | 审查维度规范 |
|
||||
| [specs/issue-classification.md](specs/issue-classification.md) | 问题分类标准 |
|
||||
| [specs/quality-standards.md](specs/quality-standards.md) | 质量标准 |
|
||||
| [templates/review-report.md](templates/review-report.md) | 报告模板 |
|
||||
| [templates/issue-template.md](templates/issue-template.md) | 问题模板 |
|
||||
@@ -0,0 +1,139 @@
|
||||
# Action: Collect Context
|
||||
|
||||
收集审查目标的上下文信息。
|
||||
|
||||
## Purpose
|
||||
|
||||
在开始审查前,收集目标代码的基本信息:
|
||||
- 确定审查范围(文件/目录)
|
||||
- 识别编程语言和框架
|
||||
- 统计代码规模
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] state.status === 'pending' || state.context === null
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state, workDir) {
|
||||
// 1. 询问用户审查目标
|
||||
const input = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: "请指定要审查的代码路径:",
|
||||
header: "审查目标",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "当前目录", description: "审查当前工作目录下的所有代码" },
|
||||
{ label: "src/", description: "审查 src/ 目录" },
|
||||
{ label: "手动指定", description: "输入自定义路径" }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
const targetPath = input["审查目标"] === "手动指定"
|
||||
? input["其他"]
|
||||
: input["审查目标"] === "当前目录" ? "." : "src/";
|
||||
|
||||
// 2. 收集文件列表
|
||||
const files = Glob(`${targetPath}/**/*.{ts,tsx,js,jsx,py,java,go,rs,cpp,c,cs}`);
|
||||
|
||||
// 3. 检测主要语言
|
||||
const languageCounts = {};
|
||||
files.forEach(file => {
|
||||
const ext = file.split('.').pop();
|
||||
const langMap = {
|
||||
'ts': 'TypeScript', 'tsx': 'TypeScript',
|
||||
'js': 'JavaScript', 'jsx': 'JavaScript',
|
||||
'py': 'Python',
|
||||
'java': 'Java',
|
||||
'go': 'Go',
|
||||
'rs': 'Rust',
|
||||
'cpp': 'C++', 'c': 'C',
|
||||
'cs': 'C#'
|
||||
};
|
||||
const lang = langMap[ext] || 'Unknown';
|
||||
languageCounts[lang] = (languageCounts[lang] || 0) + 1;
|
||||
});
|
||||
|
||||
const primaryLanguage = Object.entries(languageCounts)
|
||||
.sort((a, b) => b[1] - a[1])[0]?.[0] || 'Unknown';
|
||||
|
||||
// 4. 统计代码行数
|
||||
let totalLines = 0;
|
||||
for (const file of files.slice(0, 100)) { // 限制前100个文件
|
||||
try {
|
||||
const content = Read(file);
|
||||
totalLines += content.split('\n').length;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 5. 检测框架
|
||||
let framework = null;
|
||||
if (files.some(f => f.includes('package.json'))) {
|
||||
const pkg = JSON.parse(Read('package.json'));
|
||||
if (pkg.dependencies?.react) framework = 'React';
|
||||
else if (pkg.dependencies?.vue) framework = 'Vue';
|
||||
else if (pkg.dependencies?.angular) framework = 'Angular';
|
||||
else if (pkg.dependencies?.express) framework = 'Express';
|
||||
else if (pkg.dependencies?.next) framework = 'Next.js';
|
||||
}
|
||||
|
||||
// 6. 构建上下文
|
||||
const context = {
|
||||
target_path: targetPath,
|
||||
files: files.slice(0, 200), // 限制最多200个文件
|
||||
language: primaryLanguage,
|
||||
framework: framework,
|
||||
total_lines: totalLines,
|
||||
file_count: files.length
|
||||
};
|
||||
|
||||
// 7. 保存上下文
|
||||
Write(`${workDir}/context.json`, JSON.stringify(context, null, 2));
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: 'running',
|
||||
context: context
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: 'running',
|
||||
context: {
|
||||
target_path: targetPath,
|
||||
files: fileList,
|
||||
language: primaryLanguage,
|
||||
framework: detectedFramework,
|
||||
total_lines: totalLines,
|
||||
file_count: fileCount
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **File**: `context.json`
|
||||
- **Location**: `${workDir}/context.json`
|
||||
- **Format**: JSON
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| 路径不存在 | 提示用户重新输入 |
|
||||
| 无代码文件 | 返回错误,终止审查 |
|
||||
| 读取权限问题 | 跳过该文件,记录警告 |
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 成功: action-quick-scan
|
||||
- 失败: action-abort
|
||||
115
.claude/skills/review-code/phases/actions/action-complete.md
Normal file
115
.claude/skills/review-code/phases/actions/action-complete.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Action: Complete
|
||||
|
||||
完成审查,保存最终状态。
|
||||
|
||||
## Purpose
|
||||
|
||||
结束代码审查流程:
|
||||
- 保存最终状态
|
||||
- 输出审查摘要
|
||||
- 提供报告路径
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] state.status === 'running'
|
||||
- [ ] state.report_generated === true
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state, workDir) {
|
||||
// 1. 计算审查时长
|
||||
const duration = Date.now() - new Date(state.started_at).getTime();
|
||||
const durationMinutes = Math.round(duration / 60000);
|
||||
|
||||
// 2. 生成最终摘要
|
||||
const summary = {
|
||||
...state.summary,
|
||||
review_duration_ms: duration,
|
||||
completed_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 3. 保存最终状态
|
||||
const finalState = {
|
||||
...state,
|
||||
status: 'completed',
|
||||
completed_at: summary.completed_at,
|
||||
summary: summary
|
||||
};
|
||||
|
||||
Write(`${workDir}/state.json`, JSON.stringify(finalState, null, 2));
|
||||
|
||||
// 4. 输出摘要信息
|
||||
console.log('========================================');
|
||||
console.log(' CODE REVIEW COMPLETED');
|
||||
console.log('========================================');
|
||||
console.log('');
|
||||
console.log(`📁 审查目标: ${state.context.target_path}`);
|
||||
console.log(`📄 文件数量: ${state.context.file_count}`);
|
||||
console.log(`📝 代码行数: ${state.context.total_lines}`);
|
||||
console.log('');
|
||||
console.log('--- 问题统计 ---');
|
||||
console.log(`🔴 Critical: ${summary.critical}`);
|
||||
console.log(`🟠 High: ${summary.high}`);
|
||||
console.log(`🟡 Medium: ${summary.medium}`);
|
||||
console.log(`🔵 Low: ${summary.low}`);
|
||||
console.log(`⚪ Info: ${summary.info}`);
|
||||
console.log(`📊 Total: ${summary.total_issues}`);
|
||||
console.log('');
|
||||
console.log(`⏱️ 审查用时: ${durationMinutes} 分钟`);
|
||||
console.log('');
|
||||
console.log(`📋 报告位置: ${state.report_path}`);
|
||||
console.log('========================================');
|
||||
|
||||
// 5. 返回状态更新
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: 'completed',
|
||||
completed_at: summary.completed_at,
|
||||
summary: summary
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
status: 'completed',
|
||||
completed_at: new Date().toISOString(),
|
||||
summary: {
|
||||
total_issues: state.summary.total_issues,
|
||||
critical: state.summary.critical,
|
||||
high: state.summary.high,
|
||||
medium: state.summary.medium,
|
||||
low: state.summary.low,
|
||||
info: state.summary.info,
|
||||
review_duration_ms: duration
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **Console**: 审查完成摘要
|
||||
- **State**: 最终状态保存到 `state.json`
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| 状态保存失败 | 输出到控制台 |
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 无(终止状态)
|
||||
|
||||
## Post-Completion
|
||||
|
||||
用户可以:
|
||||
1. 查看完整报告: `cat ${workDir}/review-report.md`
|
||||
2. 查看问题详情: `cat ${workDir}/findings/*.json`
|
||||
3. 导出报告到其他位置
|
||||
256
.claude/skills/review-code/phases/actions/action-deep-review.md
Normal file
256
.claude/skills/review-code/phases/actions/action-deep-review.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# Action: Deep Review
|
||||
|
||||
深入审查指定维度的代码质量。
|
||||
|
||||
## Purpose
|
||||
|
||||
针对单个维度进行深入审查:
|
||||
- 逐文件检查
|
||||
- 记录发现的问题
|
||||
- 提供具体的修复建议
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] state.status === 'running'
|
||||
- [ ] state.scan_completed === true
|
||||
- [ ] 存在未审查的维度
|
||||
|
||||
## Dimension Focus Areas
|
||||
|
||||
### Correctness (正确性)
|
||||
- 逻辑错误和边界条件
|
||||
- Null/undefined 处理
|
||||
- 错误处理完整性
|
||||
- 类型安全
|
||||
- 资源泄漏
|
||||
|
||||
### Readability (可读性)
|
||||
- 命名规范
|
||||
- 函数长度和复杂度
|
||||
- 代码重复
|
||||
- 注释质量
|
||||
- 代码组织
|
||||
|
||||
### Performance (性能)
|
||||
- 算法复杂度
|
||||
- 不必要的计算
|
||||
- 内存使用
|
||||
- I/O 效率
|
||||
- 缓存策略
|
||||
|
||||
### Security (安全性)
|
||||
- 注入风险 (SQL, XSS, Command)
|
||||
- 认证和授权
|
||||
- 敏感数据处理
|
||||
- 加密使用
|
||||
- 依赖安全
|
||||
|
||||
### Testing (测试)
|
||||
- 测试覆盖率
|
||||
- 边界条件测试
|
||||
- 错误路径测试
|
||||
- 测试可维护性
|
||||
- Mock 使用
|
||||
|
||||
### Architecture (架构)
|
||||
- 分层结构
|
||||
- 依赖方向
|
||||
- 单一职责
|
||||
- 接口设计
|
||||
- 扩展性
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state, workDir, currentDimension) {
|
||||
const context = state.context;
|
||||
const dimension = currentDimension;
|
||||
const findings = [];
|
||||
|
||||
// 获取维度特定的检查规则
|
||||
const rules = getDimensionRules(dimension);
|
||||
|
||||
// 优先审查高风险区域
|
||||
const filesToReview = state.scan_summary?.risk_areas
|
||||
?.map(r => r.file)
|
||||
?.filter(f => context.files.includes(f)) || context.files;
|
||||
|
||||
const filesToCheck = [...new Set([
|
||||
...filesToReview.slice(0, 20),
|
||||
...context.files.slice(0, 30)
|
||||
])].slice(0, 50); // 最多50个文件
|
||||
|
||||
let findingCounter = 1;
|
||||
|
||||
for (const file of filesToCheck) {
|
||||
try {
|
||||
const content = Read(file);
|
||||
const lines = content.split('\n');
|
||||
|
||||
// 应用维度特定规则
|
||||
for (const rule of rules) {
|
||||
const matches = rule.detect(content, lines, file);
|
||||
for (const match of matches) {
|
||||
findings.push({
|
||||
id: `${getDimensionPrefix(dimension)}-${String(findingCounter++).padStart(3, '0')}`,
|
||||
severity: match.severity,
|
||||
dimension: dimension,
|
||||
category: rule.category,
|
||||
file: file,
|
||||
line: match.line,
|
||||
code_snippet: match.snippet,
|
||||
description: match.description,
|
||||
recommendation: rule.recommendation,
|
||||
fix_example: rule.fixExample
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 跳过无法读取的文件
|
||||
}
|
||||
}
|
||||
|
||||
// 保存维度发现
|
||||
Write(`${workDir}/findings/${dimension}.json`, JSON.stringify(findings, null, 2));
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
reviewed_dimensions: [...(state.reviewed_dimensions || []), dimension],
|
||||
current_dimension: null,
|
||||
[`findings.${dimension}`]: findings
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getDimensionPrefix(dimension) {
|
||||
const prefixes = {
|
||||
correctness: 'CORR',
|
||||
readability: 'READ',
|
||||
performance: 'PERF',
|
||||
security: 'SEC',
|
||||
testing: 'TEST',
|
||||
architecture: 'ARCH'
|
||||
};
|
||||
return prefixes[dimension] || 'MISC';
|
||||
}
|
||||
|
||||
function getDimensionRules(dimension) {
|
||||
// 参见 specs/review-dimensions.md 获取完整规则
|
||||
const allRules = {
|
||||
correctness: [
|
||||
{
|
||||
category: 'null-check',
|
||||
detect: (content, lines) => {
|
||||
const issues = [];
|
||||
lines.forEach((line, i) => {
|
||||
if (line.match(/\w+\.\w+/) && !line.includes('?.') && !line.includes('if') && !line.includes('null')) {
|
||||
// 简化检测:可能缺少 null 检查
|
||||
}
|
||||
});
|
||||
return issues;
|
||||
},
|
||||
recommendation: 'Add null/undefined check before accessing properties',
|
||||
fixExample: 'obj?.property or if (obj) { obj.property }'
|
||||
},
|
||||
{
|
||||
category: 'empty-catch',
|
||||
detect: (content, lines) => {
|
||||
const issues = [];
|
||||
const regex = /catch\s*\([^)]*\)\s*{\s*}/g;
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
const lineNum = content.substring(0, match.index).split('\n').length;
|
||||
issues.push({
|
||||
severity: 'high',
|
||||
line: lineNum,
|
||||
snippet: match[0],
|
||||
description: 'Empty catch block silently swallows errors'
|
||||
});
|
||||
}
|
||||
return issues;
|
||||
},
|
||||
recommendation: 'Log the error or rethrow it',
|
||||
fixExample: 'catch (e) { console.error(e); throw e; }'
|
||||
}
|
||||
],
|
||||
security: [
|
||||
{
|
||||
category: 'xss-risk',
|
||||
detect: (content, lines) => {
|
||||
const issues = [];
|
||||
lines.forEach((line, i) => {
|
||||
if (line.includes('innerHTML') || line.includes('dangerouslySetInnerHTML')) {
|
||||
issues.push({
|
||||
severity: 'critical',
|
||||
line: i + 1,
|
||||
snippet: line.trim().substring(0, 100),
|
||||
description: 'Direct HTML injection can lead to XSS vulnerabilities'
|
||||
});
|
||||
}
|
||||
});
|
||||
return issues;
|
||||
},
|
||||
recommendation: 'Use textContent or sanitize HTML before injection',
|
||||
fixExample: 'element.textContent = userInput; // or sanitize(userInput)'
|
||||
},
|
||||
{
|
||||
category: 'hardcoded-secret',
|
||||
detect: (content, lines) => {
|
||||
const issues = [];
|
||||
const regex = /(?:password|secret|api[_-]?key|token)\s*[=:]\s*['"]([^'"]{8,})['"]/gi;
|
||||
lines.forEach((line, i) => {
|
||||
if (regex.test(line)) {
|
||||
issues.push({
|
||||
severity: 'critical',
|
||||
line: i + 1,
|
||||
snippet: line.trim().substring(0, 100),
|
||||
description: 'Hardcoded credentials detected'
|
||||
});
|
||||
}
|
||||
regex.lastIndex = 0; // Reset regex
|
||||
});
|
||||
return issues;
|
||||
},
|
||||
recommendation: 'Use environment variables or secret management',
|
||||
fixExample: 'const apiKey = process.env.API_KEY;'
|
||||
}
|
||||
],
|
||||
// ... 其他维度规则参见 specs/review-dimensions.md
|
||||
};
|
||||
|
||||
return allRules[dimension] || [];
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
reviewed_dimensions: [...state.reviewed_dimensions, currentDimension],
|
||||
current_dimension: null,
|
||||
findings: {
|
||||
...state.findings,
|
||||
[currentDimension]: newFindings
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **File**: `findings/{dimension}.json`
|
||||
- **Location**: `${workDir}/findings/`
|
||||
- **Format**: JSON array of Finding objects
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| 文件读取失败 | 跳过该文件,记录警告 |
|
||||
| 规则执行错误 | 跳过该规则,继续其他规则 |
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 还有未审查维度: 继续 action-deep-review
|
||||
- 所有维度完成: action-generate-report
|
||||
@@ -0,0 +1,263 @@
|
||||
# Action: Generate Report
|
||||
|
||||
汇总所有发现,生成结构化审查报告。
|
||||
|
||||
## Purpose
|
||||
|
||||
生成最终的代码审查报告:
|
||||
- 汇总所有维度的发现
|
||||
- 按严重程度排序
|
||||
- 提供统计摘要
|
||||
- 输出 Markdown 格式报告
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] state.status === 'running'
|
||||
- [ ] 所有维度已审查完成 (reviewed_dimensions.length === 6)
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state, workDir) {
|
||||
const context = state.context;
|
||||
const findings = state.findings;
|
||||
|
||||
// 1. 汇总所有发现
|
||||
const allFindings = [
|
||||
...findings.correctness,
|
||||
...findings.readability,
|
||||
...findings.performance,
|
||||
...findings.security,
|
||||
...findings.testing,
|
||||
...findings.architecture
|
||||
];
|
||||
|
||||
// 2. 按严重程度排序
|
||||
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
|
||||
allFindings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
||||
|
||||
// 3. 统计
|
||||
const stats = {
|
||||
total_issues: allFindings.length,
|
||||
critical: allFindings.filter(f => f.severity === 'critical').length,
|
||||
high: allFindings.filter(f => f.severity === 'high').length,
|
||||
medium: allFindings.filter(f => f.severity === 'medium').length,
|
||||
low: allFindings.filter(f => f.severity === 'low').length,
|
||||
info: allFindings.filter(f => f.severity === 'info').length,
|
||||
by_dimension: {
|
||||
correctness: findings.correctness.length,
|
||||
readability: findings.readability.length,
|
||||
performance: findings.performance.length,
|
||||
security: findings.security.length,
|
||||
testing: findings.testing.length,
|
||||
architecture: findings.architecture.length
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 生成报告
|
||||
const report = generateMarkdownReport(context, stats, allFindings, state.scan_summary);
|
||||
|
||||
// 5. 保存报告
|
||||
const reportPath = `${workDir}/review-report.md`;
|
||||
Write(reportPath, report);
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
report_generated: true,
|
||||
report_path: reportPath,
|
||||
summary: {
|
||||
...stats,
|
||||
review_duration_ms: Date.now() - new Date(state.started_at).getTime()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function generateMarkdownReport(context, stats, findings, scanSummary) {
|
||||
const severityEmoji = {
|
||||
critical: '🔴',
|
||||
high: '🟠',
|
||||
medium: '🟡',
|
||||
low: '🔵',
|
||||
info: '⚪'
|
||||
};
|
||||
|
||||
let report = `# Code Review Report
|
||||
|
||||
## 审查概览
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|------|
|
||||
| 目标路径 | \`${context.target_path}\` |
|
||||
| 文件数量 | ${context.file_count} |
|
||||
| 代码行数 | ${context.total_lines} |
|
||||
| 主要语言 | ${context.language} |
|
||||
| 框架 | ${context.framework || 'N/A'} |
|
||||
|
||||
## 问题统计
|
||||
|
||||
| 严重程度 | 数量 |
|
||||
|----------|------|
|
||||
| 🔴 Critical | ${stats.critical} |
|
||||
| 🟠 High | ${stats.high} |
|
||||
| 🟡 Medium | ${stats.medium} |
|
||||
| 🔵 Low | ${stats.low} |
|
||||
| ⚪ Info | ${stats.info} |
|
||||
| **总计** | **${stats.total_issues}** |
|
||||
|
||||
### 按维度统计
|
||||
|
||||
| 维度 | 问题数 |
|
||||
|------|--------|
|
||||
| Correctness (正确性) | ${stats.by_dimension.correctness} |
|
||||
| Security (安全性) | ${stats.by_dimension.security} |
|
||||
| Performance (性能) | ${stats.by_dimension.performance} |
|
||||
| Readability (可读性) | ${stats.by_dimension.readability} |
|
||||
| Testing (测试) | ${stats.by_dimension.testing} |
|
||||
| Architecture (架构) | ${stats.by_dimension.architecture} |
|
||||
|
||||
---
|
||||
|
||||
## 高风险区域
|
||||
|
||||
`;
|
||||
|
||||
if (scanSummary?.risk_areas?.length > 0) {
|
||||
report += `| 文件 | 原因 | 优先级 |
|
||||
|------|------|--------|
|
||||
`;
|
||||
for (const area of scanSummary.risk_areas.slice(0, 10)) {
|
||||
report += `| \`${area.file}\` | ${area.reason} | ${area.priority} |\n`;
|
||||
}
|
||||
} else {
|
||||
report += `未发现明显的高风险区域。\n`;
|
||||
}
|
||||
|
||||
report += `
|
||||
---
|
||||
|
||||
## 问题详情
|
||||
|
||||
`;
|
||||
|
||||
// 按维度分组输出
|
||||
const dimensions = ['correctness', 'security', 'performance', 'readability', 'testing', 'architecture'];
|
||||
const dimensionNames = {
|
||||
correctness: '正确性 (Correctness)',
|
||||
security: '安全性 (Security)',
|
||||
performance: '性能 (Performance)',
|
||||
readability: '可读性 (Readability)',
|
||||
testing: '测试 (Testing)',
|
||||
architecture: '架构 (Architecture)'
|
||||
};
|
||||
|
||||
for (const dim of dimensions) {
|
||||
const dimFindings = findings.filter(f => f.dimension === dim);
|
||||
if (dimFindings.length === 0) continue;
|
||||
|
||||
report += `### ${dimensionNames[dim]}
|
||||
|
||||
`;
|
||||
|
||||
for (const finding of dimFindings) {
|
||||
report += `#### ${severityEmoji[finding.severity]} [${finding.id}] ${finding.category}
|
||||
|
||||
- **严重程度**: ${finding.severity.toUpperCase()}
|
||||
- **文件**: \`${finding.file}\`${finding.line ? `:${finding.line}` : ''}
|
||||
- **描述**: ${finding.description}
|
||||
`;
|
||||
|
||||
if (finding.code_snippet) {
|
||||
report += `
|
||||
\`\`\`
|
||||
${finding.code_snippet}
|
||||
\`\`\`
|
||||
`;
|
||||
}
|
||||
|
||||
report += `
|
||||
**建议**: ${finding.recommendation}
|
||||
`;
|
||||
|
||||
if (finding.fix_example) {
|
||||
report += `
|
||||
**修复示例**:
|
||||
\`\`\`
|
||||
${finding.fix_example}
|
||||
\`\`\`
|
||||
`;
|
||||
}
|
||||
|
||||
report += `
|
||||
---
|
||||
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
report += `
|
||||
## 审查建议
|
||||
|
||||
### 必须修复 (Must Fix)
|
||||
|
||||
${stats.critical + stats.high > 0
|
||||
? `发现 ${stats.critical} 个严重问题和 ${stats.high} 个高优先级问题,建议在合并前修复。`
|
||||
: '未发现必须立即修复的问题。'}
|
||||
|
||||
### 建议改进 (Should Fix)
|
||||
|
||||
${stats.medium > 0
|
||||
? `发现 ${stats.medium} 个中等优先级问题,建议在后续迭代中改进。`
|
||||
: '代码质量良好,无明显需要改进的地方。'}
|
||||
|
||||
### 可选优化 (Nice to Have)
|
||||
|
||||
${stats.low + stats.info > 0
|
||||
? `发现 ${stats.low + stats.info} 个低优先级建议,可根据团队规范酌情处理。`
|
||||
: '无额外建议。'}
|
||||
|
||||
---
|
||||
|
||||
*报告生成时间: ${new Date().toISOString()}*
|
||||
`;
|
||||
|
||||
return report;
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
report_generated: true,
|
||||
report_path: reportPath,
|
||||
summary: {
|
||||
total_issues: totalCount,
|
||||
critical: criticalCount,
|
||||
high: highCount,
|
||||
medium: mediumCount,
|
||||
low: lowCount,
|
||||
info: infoCount,
|
||||
review_duration_ms: duration
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **File**: `review-report.md`
|
||||
- **Location**: `${workDir}/review-report.md`
|
||||
- **Format**: Markdown
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| 写入失败 | 尝试备用位置 |
|
||||
| 模板错误 | 使用简化格式 |
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 成功: action-complete
|
||||
164
.claude/skills/review-code/phases/actions/action-quick-scan.md
Normal file
164
.claude/skills/review-code/phases/actions/action-quick-scan.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Action: Quick Scan
|
||||
|
||||
快速扫描代码,识别高风险区域。
|
||||
|
||||
## Purpose
|
||||
|
||||
进行第一遍快速扫描:
|
||||
- 识别复杂度高的文件
|
||||
- 标记潜在的高风险区域
|
||||
- 发现明显的问题模式
|
||||
|
||||
## Preconditions
|
||||
|
||||
- [ ] state.status === 'running'
|
||||
- [ ] state.context !== null
|
||||
|
||||
## Execution
|
||||
|
||||
```javascript
|
||||
async function execute(state, workDir) {
|
||||
const context = state.context;
|
||||
const riskAreas = [];
|
||||
const quickIssues = [];
|
||||
|
||||
// 1. 扫描每个文件
|
||||
for (const file of context.files) {
|
||||
try {
|
||||
const content = Read(file);
|
||||
const lines = content.split('\n');
|
||||
|
||||
// --- 复杂度检查 ---
|
||||
const functionMatches = content.match(/function\s+\w+|=>\s*{|async\s+\w+/g) || [];
|
||||
const nestingDepth = Math.max(...lines.map(l => (l.match(/^\s*/)?.[0].length || 0) / 2));
|
||||
|
||||
if (lines.length > 500 || functionMatches.length > 20 || nestingDepth > 8) {
|
||||
riskAreas.push({
|
||||
file: file,
|
||||
reason: `High complexity: ${lines.length} lines, ${functionMatches.length} functions, depth ${nestingDepth}`,
|
||||
priority: 'high'
|
||||
});
|
||||
}
|
||||
|
||||
// --- 快速问题检测 ---
|
||||
|
||||
// 安全问题快速检测
|
||||
if (content.includes('eval(') || content.includes('innerHTML')) {
|
||||
quickIssues.push({
|
||||
type: 'security',
|
||||
file: file,
|
||||
message: 'Potential XSS/injection risk: eval() or innerHTML usage'
|
||||
});
|
||||
}
|
||||
|
||||
// 硬编码密钥检测
|
||||
if (/(?:password|secret|api_key|token)\s*[=:]\s*['"][^'"]{8,}/i.test(content)) {
|
||||
quickIssues.push({
|
||||
type: 'security',
|
||||
file: file,
|
||||
message: 'Potential hardcoded credential detected'
|
||||
});
|
||||
}
|
||||
|
||||
// TODO/FIXME 检测
|
||||
const todoCount = (content.match(/TODO|FIXME|HACK|XXX/gi) || []).length;
|
||||
if (todoCount > 5) {
|
||||
quickIssues.push({
|
||||
type: 'maintenance',
|
||||
file: file,
|
||||
message: `${todoCount} TODO/FIXME comments found`
|
||||
});
|
||||
}
|
||||
|
||||
// console.log 检测(生产代码)
|
||||
if (!file.includes('test') && !file.includes('spec')) {
|
||||
const consoleCount = (content.match(/console\.(log|debug|info)/g) || []).length;
|
||||
if (consoleCount > 3) {
|
||||
quickIssues.push({
|
||||
type: 'readability',
|
||||
file: file,
|
||||
message: `${consoleCount} console statements (should be removed in production)`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 长函数检测
|
||||
const longFunctions = content.match(/function[^{]+\{[^}]{2000,}\}/g) || [];
|
||||
if (longFunctions.length > 0) {
|
||||
quickIssues.push({
|
||||
type: 'readability',
|
||||
file: file,
|
||||
message: `${longFunctions.length} long function(s) detected (>50 lines)`
|
||||
});
|
||||
}
|
||||
|
||||
// 错误处理检测
|
||||
if (content.includes('catch') && content.includes('catch (') && content.match(/catch\s*\([^)]*\)\s*{\s*}/)) {
|
||||
quickIssues.push({
|
||||
type: 'correctness',
|
||||
file: file,
|
||||
message: 'Empty catch block detected'
|
||||
});
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
// 跳过无法读取的文件
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 计算复杂度评分
|
||||
const complexityScore = Math.min(100, Math.round(
|
||||
(riskAreas.length * 10 + quickIssues.length * 5) / context.file_count * 100
|
||||
));
|
||||
|
||||
// 3. 构建扫描摘要
|
||||
const scanSummary = {
|
||||
risk_areas: riskAreas,
|
||||
complexity_score: complexityScore,
|
||||
quick_issues: quickIssues
|
||||
};
|
||||
|
||||
// 4. 保存扫描结果
|
||||
Write(`${workDir}/scan-summary.json`, JSON.stringify(scanSummary, null, 2));
|
||||
|
||||
return {
|
||||
stateUpdates: {
|
||||
scan_completed: true,
|
||||
scan_summary: scanSummary
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## State Updates
|
||||
|
||||
```javascript
|
||||
return {
|
||||
stateUpdates: {
|
||||
scan_completed: true,
|
||||
scan_summary: {
|
||||
risk_areas: riskAreas,
|
||||
complexity_score: score,
|
||||
quick_issues: quickIssues
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **File**: `scan-summary.json`
|
||||
- **Location**: `${workDir}/scan-summary.json`
|
||||
- **Format**: JSON
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| 文件读取失败 | 跳过该文件,继续扫描 |
|
||||
| 编码问题 | 以二进制跳过 |
|
||||
|
||||
## Next Actions
|
||||
|
||||
- 成功: action-deep-review (开始逐维度审查)
|
||||
- 风险区域过多 (>20): 可询问用户是否缩小范围
|
||||
192
.claude/skills/review-code/phases/orchestrator.md
Normal file
192
.claude/skills/review-code/phases/orchestrator.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Orchestrator
|
||||
|
||||
根据当前状态选择并执行下一个审查动作。
|
||||
|
||||
## Role
|
||||
|
||||
Code Review 编排器,负责:
|
||||
1. 读取当前审查状态
|
||||
2. 根据状态选择下一个动作
|
||||
3. 执行动作并更新状态
|
||||
4. 循环直到审查完成
|
||||
|
||||
## State Management
|
||||
|
||||
### 读取状态
|
||||
|
||||
```javascript
|
||||
const state = JSON.parse(Read(`${workDir}/state.json`));
|
||||
```
|
||||
|
||||
### 更新状态
|
||||
|
||||
```javascript
|
||||
function updateState(updates) {
|
||||
const state = JSON.parse(Read(`${workDir}/state.json`));
|
||||
const newState = {
|
||||
...state,
|
||||
...updates,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
Write(`${workDir}/state.json`, JSON.stringify(newState, null, 2));
|
||||
return newState;
|
||||
}
|
||||
```
|
||||
|
||||
## Decision Logic
|
||||
|
||||
```javascript
|
||||
function selectNextAction(state) {
|
||||
// 1. 终止条件检查
|
||||
if (state.status === 'completed') return null;
|
||||
if (state.status === 'user_exit') return null;
|
||||
if (state.error_count >= 3) return 'action-abort';
|
||||
|
||||
// 2. 初始化阶段
|
||||
if (state.status === 'pending' || !state.context) {
|
||||
return 'action-collect-context';
|
||||
}
|
||||
|
||||
// 3. 快速扫描阶段
|
||||
if (!state.scan_completed) {
|
||||
return 'action-quick-scan';
|
||||
}
|
||||
|
||||
// 4. 深入审查阶段 - 逐维度审查
|
||||
const dimensions = ['correctness', 'readability', 'performance', 'security', 'testing', 'architecture'];
|
||||
const reviewedDimensions = state.reviewed_dimensions || [];
|
||||
|
||||
for (const dim of dimensions) {
|
||||
if (!reviewedDimensions.includes(dim)) {
|
||||
return 'action-deep-review'; // 传递 dimension 参数
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 报告生成阶段
|
||||
if (!state.report_generated) {
|
||||
return 'action-generate-report';
|
||||
}
|
||||
|
||||
// 6. 完成
|
||||
return 'action-complete';
|
||||
}
|
||||
```
|
||||
|
||||
## Execution Loop
|
||||
|
||||
```javascript
|
||||
async function runOrchestrator() {
|
||||
console.log('=== Code Review Orchestrator Started ===');
|
||||
|
||||
let iteration = 0;
|
||||
const MAX_ITERATIONS = 20; // 6 dimensions + overhead
|
||||
|
||||
while (iteration < MAX_ITERATIONS) {
|
||||
iteration++;
|
||||
|
||||
// 1. 读取当前状态
|
||||
const state = JSON.parse(Read(`${workDir}/state.json`));
|
||||
console.log(`[Iteration ${iteration}] Status: ${state.status}`);
|
||||
|
||||
// 2. 选择下一个动作
|
||||
const actionId = selectNextAction(state);
|
||||
|
||||
if (!actionId) {
|
||||
console.log('Review completed, terminating.');
|
||||
break;
|
||||
}
|
||||
|
||||
console.log(`[Iteration ${iteration}] Executing: ${actionId}`);
|
||||
|
||||
// 3. 更新状态:当前动作
|
||||
updateState({ current_action: actionId });
|
||||
|
||||
// 4. 执行动作
|
||||
try {
|
||||
const actionPrompt = Read(`phases/actions/${actionId}.md`);
|
||||
|
||||
// 确定当前需要审查的维度
|
||||
let currentDimension = null;
|
||||
if (actionId === 'action-deep-review') {
|
||||
const dimensions = ['correctness', 'readability', 'performance', 'security', 'testing', 'architecture'];
|
||||
const reviewed = state.reviewed_dimensions || [];
|
||||
currentDimension = dimensions.find(d => !reviewed.includes(d));
|
||||
}
|
||||
|
||||
const result = await Task({
|
||||
subagent_type: 'universal-executor',
|
||||
run_in_background: false,
|
||||
prompt: `
|
||||
[WORK_DIR]
|
||||
${workDir}
|
||||
|
||||
[STATE]
|
||||
${JSON.stringify(state, null, 2)}
|
||||
|
||||
[CURRENT_DIMENSION]
|
||||
${currentDimension || 'N/A'}
|
||||
|
||||
[ACTION]
|
||||
${actionPrompt}
|
||||
|
||||
[SPECS]
|
||||
Review Dimensions: specs/review-dimensions.md
|
||||
Issue Classification: specs/issue-classification.md
|
||||
|
||||
[RETURN]
|
||||
Return JSON with stateUpdates field containing updates to apply to state.
|
||||
`
|
||||
});
|
||||
|
||||
const actionResult = JSON.parse(result);
|
||||
|
||||
// 5. 更新状态:动作完成
|
||||
updateState({
|
||||
current_action: null,
|
||||
completed_actions: [...(state.completed_actions || []), actionId],
|
||||
...actionResult.stateUpdates
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
// 错误处理
|
||||
updateState({
|
||||
current_action: null,
|
||||
errors: [...(state.errors || []), {
|
||||
action: actionId,
|
||||
message: error.message,
|
||||
timestamp: new Date().toISOString()
|
||||
}],
|
||||
error_count: (state.error_count || 0) + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log('=== Code Review Orchestrator Finished ===');
|
||||
}
|
||||
```
|
||||
|
||||
## Action Catalog
|
||||
|
||||
| Action | Purpose | Preconditions |
|
||||
|--------|---------|---------------|
|
||||
| [action-collect-context](actions/action-collect-context.md) | 收集审查目标上下文 | status === 'pending' |
|
||||
| [action-quick-scan](actions/action-quick-scan.md) | 快速扫描识别风险区域 | context !== null |
|
||||
| [action-deep-review](actions/action-deep-review.md) | 深入审查指定维度 | scan_completed === true |
|
||||
| [action-generate-report](actions/action-generate-report.md) | 生成结构化审查报告 | all dimensions reviewed |
|
||||
| [action-complete](actions/action-complete.md) | 完成审查,保存结果 | report_generated === true |
|
||||
|
||||
## Termination Conditions
|
||||
|
||||
- `state.status === 'completed'` - 审查正常完成
|
||||
- `state.status === 'user_exit'` - 用户主动退出
|
||||
- `state.error_count >= 3` - 错误次数超限
|
||||
- `iteration >= MAX_ITERATIONS` - 迭代次数超限
|
||||
|
||||
## Error Recovery
|
||||
|
||||
| Error Type | Recovery Strategy |
|
||||
|------------|-------------------|
|
||||
| 文件读取失败 | 跳过该文件,记录警告 |
|
||||
| 动作执行失败 | 重试最多 3 次 |
|
||||
| 状态不一致 | 重新初始化状态 |
|
||||
| 用户中止 | 保存当前进度,允许恢复 |
|
||||
174
.claude/skills/review-code/phases/state-schema.md
Normal file
174
.claude/skills/review-code/phases/state-schema.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# State Schema
|
||||
|
||||
Code Review 状态结构定义。
|
||||
|
||||
## Schema Definition
|
||||
|
||||
```typescript
|
||||
interface ReviewState {
|
||||
// === 元数据 ===
|
||||
status: 'pending' | 'running' | 'completed' | 'failed' | 'user_exit';
|
||||
started_at: string; // ISO timestamp
|
||||
updated_at: string; // ISO timestamp
|
||||
completed_at?: string; // ISO timestamp
|
||||
|
||||
// === 审查目标 ===
|
||||
context: {
|
||||
target_path: string; // 目标路径(文件或目录)
|
||||
files: string[]; // 待审查文件列表
|
||||
language: string; // 主要编程语言
|
||||
framework?: string; // 框架(如有)
|
||||
total_lines: number; // 总代码行数
|
||||
file_count: number; // 文件数量
|
||||
};
|
||||
|
||||
// === 扫描结果 ===
|
||||
scan_completed: boolean;
|
||||
scan_summary: {
|
||||
risk_areas: RiskArea[]; // 高风险区域
|
||||
complexity_score: number; // 复杂度评分
|
||||
quick_issues: QuickIssue[]; // 快速发现的问题
|
||||
};
|
||||
|
||||
// === 审查进度 ===
|
||||
reviewed_dimensions: string[]; // 已完成的审查维度
|
||||
current_dimension?: string; // 当前审查维度
|
||||
|
||||
// === 发现的问题 ===
|
||||
findings: {
|
||||
correctness: Finding[];
|
||||
readability: Finding[];
|
||||
performance: Finding[];
|
||||
security: Finding[];
|
||||
testing: Finding[];
|
||||
architecture: Finding[];
|
||||
};
|
||||
|
||||
// === 报告状态 ===
|
||||
report_generated: boolean;
|
||||
report_path?: string;
|
||||
|
||||
// === 执行跟踪 ===
|
||||
current_action?: string;
|
||||
completed_actions: string[];
|
||||
errors: ExecutionError[];
|
||||
error_count: number;
|
||||
|
||||
// === 统计信息 ===
|
||||
summary?: {
|
||||
total_issues: number;
|
||||
critical: number;
|
||||
high: number;
|
||||
medium: number;
|
||||
low: number;
|
||||
info: number;
|
||||
review_duration_ms: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface RiskArea {
|
||||
file: string;
|
||||
reason: string;
|
||||
priority: 'high' | 'medium' | 'low';
|
||||
}
|
||||
|
||||
interface QuickIssue {
|
||||
type: string;
|
||||
file: string;
|
||||
line?: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface Finding {
|
||||
id: string; // 唯一标识 e.g., "CORR-001"
|
||||
severity: 'critical' | 'high' | 'medium' | 'low' | 'info';
|
||||
dimension: string; // 所属维度
|
||||
category: string; // 问题类别
|
||||
file: string; // 文件路径
|
||||
line?: number; // 行号
|
||||
column?: number; // 列号
|
||||
code_snippet?: string; // 问题代码片段
|
||||
description: string; // 问题描述
|
||||
recommendation: string; // 修复建议
|
||||
fix_example?: string; // 修复示例代码
|
||||
references?: string[]; // 参考资料链接
|
||||
}
|
||||
|
||||
interface ExecutionError {
|
||||
action: string;
|
||||
message: string;
|
||||
timestamp: string;
|
||||
}
|
||||
```
|
||||
|
||||
## Initial State
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "pending",
|
||||
"started_at": "2024-01-01T00:00:00.000Z",
|
||||
"updated_at": "2024-01-01T00:00:00.000Z",
|
||||
"context": null,
|
||||
"scan_completed": false,
|
||||
"scan_summary": null,
|
||||
"reviewed_dimensions": [],
|
||||
"current_dimension": null,
|
||||
"findings": {
|
||||
"correctness": [],
|
||||
"readability": [],
|
||||
"performance": [],
|
||||
"security": [],
|
||||
"testing": [],
|
||||
"architecture": []
|
||||
},
|
||||
"report_generated": false,
|
||||
"report_path": null,
|
||||
"current_action": null,
|
||||
"completed_actions": [],
|
||||
"errors": [],
|
||||
"error_count": 0,
|
||||
"summary": null
|
||||
}
|
||||
```
|
||||
|
||||
## State Transitions
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> pending: Initialize
|
||||
pending --> running: collect-context
|
||||
running --> running: quick-scan
|
||||
running --> running: deep-review (6x)
|
||||
running --> running: generate-report
|
||||
running --> completed: complete
|
||||
running --> failed: error_count >= 3
|
||||
running --> user_exit: User abort
|
||||
completed --> [*]
|
||||
failed --> [*]
|
||||
user_exit --> [*]
|
||||
```
|
||||
|
||||
## Dimension Review Order
|
||||
|
||||
1. **correctness** - 正确性(最高优先级)
|
||||
2. **security** - 安全性(关键)
|
||||
3. **performance** - 性能
|
||||
4. **readability** - 可读性
|
||||
5. **testing** - 测试覆盖
|
||||
6. **architecture** - 架构一致性
|
||||
|
||||
## Finding ID Format
|
||||
|
||||
```
|
||||
{DIMENSION_PREFIX}-{SEQUENCE}
|
||||
|
||||
Prefixes:
|
||||
- CORR: Correctness
|
||||
- READ: Readability
|
||||
- PERF: Performance
|
||||
- SEC: Security
|
||||
- TEST: Testing
|
||||
- ARCH: Architecture
|
||||
|
||||
Example: SEC-003 = Security issue #3
|
||||
```
|
||||
228
.claude/skills/review-code/specs/issue-classification.md
Normal file
228
.claude/skills/review-code/specs/issue-classification.md
Normal file
@@ -0,0 +1,228 @@
|
||||
# Issue Classification
|
||||
|
||||
问题分类和严重程度标准。
|
||||
|
||||
## When to Use
|
||||
|
||||
| Phase | Usage | Section |
|
||||
|-------|-------|---------|
|
||||
| action-deep-review | 确定问题严重程度 | Severity Levels |
|
||||
| action-generate-report | 问题分类展示 | Category Mapping |
|
||||
|
||||
---
|
||||
|
||||
## Severity Levels
|
||||
|
||||
### Critical (严重) 🔴
|
||||
|
||||
**定义**: 必须在合并前修复的阻塞性问题
|
||||
|
||||
**标准**:
|
||||
- 安全漏洞 (可被利用)
|
||||
- 数据损坏或丢失风险
|
||||
- 系统崩溃风险
|
||||
- 生产环境重大故障
|
||||
|
||||
**示例**:
|
||||
- SQL/XSS/命令注入
|
||||
- 硬编码密钥泄露
|
||||
- 未捕获的异常导致崩溃
|
||||
- 数据库事务未正确处理
|
||||
|
||||
**响应**: 必须立即修复,阻塞合并
|
||||
|
||||
---
|
||||
|
||||
### High (高) 🟠
|
||||
|
||||
**定义**: 应在合并前修复的重要问题
|
||||
|
||||
**标准**:
|
||||
- 功能缺陷
|
||||
- 重要边界条件未处理
|
||||
- 性能严重退化
|
||||
- 资源泄漏
|
||||
|
||||
**示例**:
|
||||
- 核心业务逻辑错误
|
||||
- 内存泄漏
|
||||
- N+1 查询问题
|
||||
- 缺少必要的错误处理
|
||||
|
||||
**响应**: 强烈建议修复
|
||||
|
||||
---
|
||||
|
||||
### Medium (中) 🟡
|
||||
|
||||
**定义**: 建议修复的代码质量问题
|
||||
|
||||
**标准**:
|
||||
- 代码可维护性问题
|
||||
- 轻微性能问题
|
||||
- 测试覆盖不足
|
||||
- 不符合团队规范
|
||||
|
||||
**示例**:
|
||||
- 函数过长
|
||||
- 命名不清晰
|
||||
- 缺少注释
|
||||
- 代码重复
|
||||
|
||||
**响应**: 建议在后续迭代修复
|
||||
|
||||
---
|
||||
|
||||
### Low (低) 🔵
|
||||
|
||||
**定义**: 可选优化的问题
|
||||
|
||||
**标准**:
|
||||
- 风格问题
|
||||
- 微小优化
|
||||
- 可读性改进
|
||||
|
||||
**示例**:
|
||||
- 变量声明顺序
|
||||
- 额外的空行
|
||||
- 可以更简洁的写法
|
||||
|
||||
**响应**: 可根据团队偏好处理
|
||||
|
||||
---
|
||||
|
||||
### Info (信息) ⚪
|
||||
|
||||
**定义**: 信息性建议,非问题
|
||||
|
||||
**标准**:
|
||||
- 学习机会
|
||||
- 替代方案建议
|
||||
- 文档完善建议
|
||||
|
||||
**示例**:
|
||||
- "这里可以考虑使用新的 API"
|
||||
- "建议添加 JSDoc 注释"
|
||||
- "可以参考 xxx 模式"
|
||||
|
||||
**响应**: 仅供参考
|
||||
|
||||
---
|
||||
|
||||
## Category Mapping
|
||||
|
||||
### By Dimension
|
||||
|
||||
| Dimension | Common Categories |
|
||||
|-----------|-------------------|
|
||||
| Correctness | `null-check`, `boundary`, `error-handling`, `type-safety`, `logic-error` |
|
||||
| Security | `injection`, `xss`, `hardcoded-secret`, `auth`, `sensitive-data` |
|
||||
| Performance | `complexity`, `n+1-query`, `memory-leak`, `blocking-io`, `inefficient-algorithm` |
|
||||
| Readability | `naming`, `function-length`, `complexity`, `comments`, `duplication` |
|
||||
| Testing | `coverage`, `boundary-test`, `mock-abuse`, `test-isolation` |
|
||||
| Architecture | `layer-violation`, `circular-dependency`, `coupling`, `srp-violation` |
|
||||
|
||||
### Category Details
|
||||
|
||||
#### Correctness Categories
|
||||
|
||||
| Category | Description | Default Severity |
|
||||
|----------|-------------|------------------|
|
||||
| `null-check` | 缺少空值检查 | High |
|
||||
| `boundary` | 边界条件未处理 | High |
|
||||
| `error-handling` | 错误处理不当 | High |
|
||||
| `type-safety` | 类型安全问题 | Medium |
|
||||
| `logic-error` | 逻辑错误 | Critical/High |
|
||||
| `resource-leak` | 资源泄漏 | High |
|
||||
|
||||
#### Security Categories
|
||||
|
||||
| Category | Description | Default Severity |
|
||||
|----------|-------------|------------------|
|
||||
| `injection` | 注入风险 (SQL/Command) | Critical |
|
||||
| `xss` | 跨站脚本风险 | Critical |
|
||||
| `hardcoded-secret` | 硬编码密钥 | Critical |
|
||||
| `auth` | 认证授权问题 | High |
|
||||
| `sensitive-data` | 敏感数据暴露 | High |
|
||||
| `insecure-dependency` | 不安全依赖 | Medium |
|
||||
|
||||
#### Performance Categories
|
||||
|
||||
| Category | Description | Default Severity |
|
||||
|----------|-------------|------------------|
|
||||
| `complexity` | 高算法复杂度 | Medium |
|
||||
| `n+1-query` | N+1 查询问题 | High |
|
||||
| `memory-leak` | 内存泄漏 | High |
|
||||
| `blocking-io` | 阻塞 I/O | Medium |
|
||||
| `inefficient-algorithm` | 低效算法 | Medium |
|
||||
| `missing-cache` | 缺少缓存 | Low |
|
||||
|
||||
#### Readability Categories
|
||||
|
||||
| Category | Description | Default Severity |
|
||||
|----------|-------------|------------------|
|
||||
| `naming` | 命名问题 | Medium |
|
||||
| `function-length` | 函数过长 | Medium |
|
||||
| `nesting-depth` | 嵌套过深 | Medium |
|
||||
| `comments` | 注释问题 | Low |
|
||||
| `duplication` | 代码重复 | Medium |
|
||||
| `magic-number` | 魔法数字 | Low |
|
||||
|
||||
#### Testing Categories
|
||||
|
||||
| Category | Description | Default Severity |
|
||||
|----------|-------------|------------------|
|
||||
| `coverage` | 测试覆盖不足 | Medium |
|
||||
| `boundary-test` | 缺少边界测试 | Medium |
|
||||
| `mock-abuse` | Mock 过度使用 | Low |
|
||||
| `test-isolation` | 测试不独立 | Medium |
|
||||
| `flaky-test` | 不稳定测试 | High |
|
||||
|
||||
#### Architecture Categories
|
||||
|
||||
| Category | Description | Default Severity |
|
||||
|----------|-------------|------------------|
|
||||
| `layer-violation` | 层次违规 | Medium |
|
||||
| `circular-dependency` | 循环依赖 | High |
|
||||
| `coupling` | 耦合过紧 | Medium |
|
||||
| `srp-violation` | 单一职责违规 | Medium |
|
||||
| `god-class` | 上帝类 | High |
|
||||
|
||||
---
|
||||
|
||||
## Finding ID Format
|
||||
|
||||
```
|
||||
{PREFIX}-{NNN}
|
||||
|
||||
Prefixes by Dimension:
|
||||
- CORR: Correctness
|
||||
- SEC: Security
|
||||
- PERF: Performance
|
||||
- READ: Readability
|
||||
- TEST: Testing
|
||||
- ARCH: Architecture
|
||||
|
||||
Examples:
|
||||
- SEC-001: First security finding
|
||||
- CORR-015: 15th correctness finding
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quality Gates
|
||||
|
||||
| Gate | Condition | Action |
|
||||
|------|-----------|--------|
|
||||
| **Block** | Critical > 0 | 禁止合并 |
|
||||
| **Warn** | High > 0 | 需要审批 |
|
||||
| **Pass** | Critical = 0, High = 0 | 允许合并 |
|
||||
|
||||
### Recommended Thresholds
|
||||
|
||||
| Metric | Ideal | Acceptable | Needs Work |
|
||||
|--------|-------|------------|------------|
|
||||
| Critical | 0 | 0 | Any > 0 |
|
||||
| High | 0 | ≤ 2 | > 2 |
|
||||
| Medium | ≤ 5 | ≤ 10 | > 10 |
|
||||
| Total | ≤ 10 | ≤ 20 | > 20 |
|
||||
214
.claude/skills/review-code/specs/quality-standards.md
Normal file
214
.claude/skills/review-code/specs/quality-standards.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# Quality Standards
|
||||
|
||||
代码审查质量标准。
|
||||
|
||||
## When to Use
|
||||
|
||||
| Phase | Usage | Section |
|
||||
|-------|-------|---------|
|
||||
| action-generate-report | 质量评估 | Quality Dimensions |
|
||||
| action-complete | 最终评分 | Quality Gates |
|
||||
|
||||
---
|
||||
|
||||
## Quality Dimensions
|
||||
|
||||
### 1. Completeness (完整性) - 25%
|
||||
|
||||
**评估审查覆盖的完整程度**
|
||||
|
||||
| Score | Criteria |
|
||||
|-------|----------|
|
||||
| 100% | 所有维度审查完成,所有高风险文件检查 |
|
||||
| 80% | 核心维度完成,主要文件检查 |
|
||||
| 60% | 部分维度完成 |
|
||||
| < 60% | 审查不完整 |
|
||||
|
||||
**检查点**:
|
||||
- [ ] 6 个维度全部审查
|
||||
- [ ] 高风险区域重点检查
|
||||
- [ ] 关键文件覆盖
|
||||
|
||||
---
|
||||
|
||||
### 2. Accuracy (准确性) - 25%
|
||||
|
||||
**评估发现问题的准确程度**
|
||||
|
||||
| Score | Criteria |
|
||||
|-------|----------|
|
||||
| 100% | 问题定位准确,分类正确,无误报 |
|
||||
| 80% | 偶有分类偏差,定位准确 |
|
||||
| 60% | 存在误报或漏报 |
|
||||
| < 60% | 准确性差 |
|
||||
|
||||
**检查点**:
|
||||
- [ ] 问题行号准确
|
||||
- [ ] 严重程度合理
|
||||
- [ ] 分类正确
|
||||
|
||||
---
|
||||
|
||||
### 3. Actionability (可操作性) - 25%
|
||||
|
||||
**评估建议的实用程度**
|
||||
|
||||
| Score | Criteria |
|
||||
|-------|----------|
|
||||
| 100% | 每个问题都有具体可执行的修复建议 |
|
||||
| 80% | 大部分问题有清晰建议 |
|
||||
| 60% | 建议较笼统 |
|
||||
| < 60% | 缺乏可操作建议 |
|
||||
|
||||
**检查点**:
|
||||
- [ ] 提供具体修复建议
|
||||
- [ ] 包含代码示例
|
||||
- [ ] 说明修复优先级
|
||||
|
||||
---
|
||||
|
||||
### 4. Consistency (一致性) - 25%
|
||||
|
||||
**评估审查标准的一致程度**
|
||||
|
||||
| Score | Criteria |
|
||||
|-------|----------|
|
||||
| 100% | 相同问题相同处理,标准统一 |
|
||||
| 80% | 基本一致,偶有差异 |
|
||||
| 60% | 标准不太统一 |
|
||||
| < 60% | 标准混乱 |
|
||||
|
||||
**检查点**:
|
||||
- [ ] ID 格式统一
|
||||
- [ ] 严重程度标准一致
|
||||
- [ ] 描述风格统一
|
||||
|
||||
---
|
||||
|
||||
## Quality Gates
|
||||
|
||||
### Review Quality Gate
|
||||
|
||||
| Gate | Overall Score | Action |
|
||||
|------|---------------|--------|
|
||||
| **Excellent** | ≥ 90% | 高质量审查 |
|
||||
| **Good** | ≥ 80% | 合格审查 |
|
||||
| **Acceptable** | ≥ 70% | 基本可接受 |
|
||||
| **Needs Improvement** | < 70% | 需要改进 |
|
||||
|
||||
### Code Quality Gate (Based on Findings)
|
||||
|
||||
| Gate | Condition | Recommendation |
|
||||
|------|-----------|----------------|
|
||||
| **Block** | Critical > 0 | 禁止合并,必须修复 |
|
||||
| **Warn** | High > 3 | 需要团队讨论 |
|
||||
| **Caution** | Medium > 10 | 建议改进 |
|
||||
| **Pass** | 其他 | 可以合并 |
|
||||
|
||||
---
|
||||
|
||||
## Report Quality Checklist
|
||||
|
||||
### Structure
|
||||
|
||||
- [ ] 包含审查概览
|
||||
- [ ] 包含问题统计
|
||||
- [ ] 包含高风险区域
|
||||
- [ ] 包含问题详情
|
||||
- [ ] 包含修复建议
|
||||
|
||||
### Content
|
||||
|
||||
- [ ] 问题描述清晰
|
||||
- [ ] 文件位置准确
|
||||
- [ ] 代码片段有效
|
||||
- [ ] 修复建议具体
|
||||
- [ ] 优先级明确
|
||||
|
||||
### Format
|
||||
|
||||
- [ ] Markdown 格式正确
|
||||
- [ ] 表格对齐
|
||||
- [ ] 代码块语法正确
|
||||
- [ ] 链接有效
|
||||
- [ ] 无拼写错误
|
||||
|
||||
---
|
||||
|
||||
## Validation Function
|
||||
|
||||
```javascript
|
||||
function validateReviewQuality(state) {
|
||||
const scores = {
|
||||
completeness: 0,
|
||||
accuracy: 0,
|
||||
actionability: 0,
|
||||
consistency: 0
|
||||
};
|
||||
|
||||
// 1. Completeness
|
||||
const dimensionsReviewed = state.reviewed_dimensions?.length || 0;
|
||||
scores.completeness = (dimensionsReviewed / 6) * 100;
|
||||
|
||||
// 2. Accuracy (需要人工验证或后续反馈)
|
||||
// 暂时基于有无错误来估算
|
||||
scores.accuracy = state.error_count === 0 ? 100 : Math.max(0, 100 - state.error_count * 20);
|
||||
|
||||
// 3. Actionability
|
||||
const findings = Object.values(state.findings).flat();
|
||||
const withRecommendations = findings.filter(f => f.recommendation).length;
|
||||
scores.actionability = findings.length > 0
|
||||
? (withRecommendations / findings.length) * 100
|
||||
: 100;
|
||||
|
||||
// 4. Consistency (检查 ID 格式等)
|
||||
const validIds = findings.filter(f => /^(CORR|SEC|PERF|READ|TEST|ARCH)-\d{3}$/.test(f.id)).length;
|
||||
scores.consistency = findings.length > 0
|
||||
? (validIds / findings.length) * 100
|
||||
: 100;
|
||||
|
||||
// Overall
|
||||
const overall = (
|
||||
scores.completeness * 0.25 +
|
||||
scores.accuracy * 0.25 +
|
||||
scores.actionability * 0.25 +
|
||||
scores.consistency * 0.25
|
||||
);
|
||||
|
||||
return {
|
||||
scores,
|
||||
overall,
|
||||
gate: overall >= 90 ? 'excellent' :
|
||||
overall >= 80 ? 'good' :
|
||||
overall >= 70 ? 'acceptable' : 'needs_improvement'
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Improvement Recommendations
|
||||
|
||||
### If Completeness is Low
|
||||
|
||||
- 增加扫描的文件范围
|
||||
- 确保所有维度都被审查
|
||||
- 重点关注高风险区域
|
||||
|
||||
### If Accuracy is Low
|
||||
|
||||
- 提高规则精度
|
||||
- 减少误报
|
||||
- 验证行号准确性
|
||||
|
||||
### If Actionability is Low
|
||||
|
||||
- 为每个问题添加修复建议
|
||||
- 提供代码示例
|
||||
- 说明修复步骤
|
||||
|
||||
### If Consistency is Low
|
||||
|
||||
- 统一 ID 格式
|
||||
- 标准化严重程度判定
|
||||
- 使用模板化描述
|
||||
337
.claude/skills/review-code/specs/review-dimensions.md
Normal file
337
.claude/skills/review-code/specs/review-dimensions.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# Review Dimensions
|
||||
|
||||
代码审查维度定义和检查点规范。
|
||||
|
||||
## When to Use
|
||||
|
||||
| Phase | Usage | Section |
|
||||
|-------|-------|---------|
|
||||
| action-deep-review | 获取维度检查规则 | All |
|
||||
| action-generate-report | 维度名称映射 | Dimension Names |
|
||||
|
||||
---
|
||||
|
||||
## Dimension Overview
|
||||
|
||||
| Dimension | Weight | Focus | Key Indicators |
|
||||
|-----------|--------|-------|----------------|
|
||||
| **Correctness** | 25% | 功能正确性 | 边界条件、错误处理、类型安全 |
|
||||
| **Security** | 25% | 安全风险 | 注入攻击、敏感数据、权限 |
|
||||
| **Performance** | 15% | 执行效率 | 算法复杂度、资源使用 |
|
||||
| **Readability** | 15% | 可维护性 | 命名、结构、注释 |
|
||||
| **Testing** | 10% | 测试质量 | 覆盖率、边界测试 |
|
||||
| **Architecture** | 10% | 架构一致性 | 分层、依赖、模式 |
|
||||
|
||||
---
|
||||
|
||||
## 1. Correctness (正确性)
|
||||
|
||||
### 检查清单
|
||||
|
||||
- [ ] **边界条件处理**
|
||||
- 空数组/空字符串
|
||||
- Null/Undefined
|
||||
- 数值边界 (0, 负数, MAX_INT)
|
||||
- 集合边界 (首元素, 末元素)
|
||||
|
||||
- [ ] **错误处理**
|
||||
- Try-catch 覆盖
|
||||
- 错误不被静默吞掉
|
||||
- 错误信息有意义
|
||||
- 资源正确释放
|
||||
|
||||
- [ ] **类型安全**
|
||||
- 类型转换正确
|
||||
- 避免隐式转换
|
||||
- TypeScript strict mode
|
||||
|
||||
- [ ] **逻辑完整性**
|
||||
- If-else 分支完整
|
||||
- Switch 有 default
|
||||
- 循环终止条件正确
|
||||
|
||||
### 常见问题模式
|
||||
|
||||
```javascript
|
||||
// ❌ 问题: 未检查 null
|
||||
function getName(user) {
|
||||
return user.name.toUpperCase(); // user 可能为 null
|
||||
}
|
||||
|
||||
// ✅ 修复
|
||||
function getName(user) {
|
||||
return user?.name?.toUpperCase() ?? 'Unknown';
|
||||
}
|
||||
|
||||
// ❌ 问题: 空 catch 块
|
||||
try {
|
||||
await fetchData();
|
||||
} catch (e) {} // 错误被静默吞掉
|
||||
|
||||
// ✅ 修复
|
||||
try {
|
||||
await fetchData();
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch data:', e);
|
||||
throw e;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Security (安全性)
|
||||
|
||||
### 检查清单
|
||||
|
||||
- [ ] **注入防护**
|
||||
- SQL 注入 (使用参数化查询)
|
||||
- XSS (避免 innerHTML)
|
||||
- 命令注入 (避免 exec)
|
||||
- 路径遍历
|
||||
|
||||
- [ ] **认证授权**
|
||||
- 权限检查完整
|
||||
- Token 验证
|
||||
- Session 管理
|
||||
|
||||
- [ ] **敏感数据**
|
||||
- 无硬编码密钥
|
||||
- 日志不含敏感信息
|
||||
- 传输加密
|
||||
|
||||
- [ ] **依赖安全**
|
||||
- 无已知漏洞依赖
|
||||
- 版本锁定
|
||||
|
||||
### 常见问题模式
|
||||
|
||||
```javascript
|
||||
// ❌ 问题: SQL 注入风险
|
||||
const query = `SELECT * FROM users WHERE id = ${userId}`;
|
||||
|
||||
// ✅ 修复: 参数化查询
|
||||
const query = `SELECT * FROM users WHERE id = ?`;
|
||||
db.query(query, [userId]);
|
||||
|
||||
// ❌ 问题: XSS 风险
|
||||
element.innerHTML = userInput;
|
||||
|
||||
// ✅ 修复
|
||||
element.textContent = userInput;
|
||||
|
||||
// ❌ 问题: 硬编码密钥
|
||||
const apiKey = 'sk-xxxxxxxxxxxx';
|
||||
|
||||
// ✅ 修复
|
||||
const apiKey = process.env.API_KEY;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Performance (性能)
|
||||
|
||||
### 检查清单
|
||||
|
||||
- [ ] **算法复杂度**
|
||||
- 避免 O(n²) 在大数据集
|
||||
- 使用合适的数据结构
|
||||
- 避免不必要的循环
|
||||
|
||||
- [ ] **I/O 效率**
|
||||
- 批量操作 vs 循环单条
|
||||
- 避免 N+1 查询
|
||||
- 适当使用缓存
|
||||
|
||||
- [ ] **资源使用**
|
||||
- 内存泄漏
|
||||
- 连接池使用
|
||||
- 大文件流式处理
|
||||
|
||||
- [ ] **异步处理**
|
||||
- 并行 vs 串行
|
||||
- Promise.all 使用
|
||||
- 避免阻塞
|
||||
|
||||
### 常见问题模式
|
||||
|
||||
```javascript
|
||||
// ❌ 问题: N+1 查询
|
||||
for (const user of users) {
|
||||
const posts = await db.query('SELECT * FROM posts WHERE user_id = ?', [user.id]);
|
||||
}
|
||||
|
||||
// ✅ 修复: 批量查询
|
||||
const userIds = users.map(u => u.id);
|
||||
const posts = await db.query('SELECT * FROM posts WHERE user_id IN (?)', [userIds]);
|
||||
|
||||
// ❌ 问题: 串行执行可并行操作
|
||||
const a = await fetchA();
|
||||
const b = await fetchB();
|
||||
const c = await fetchC();
|
||||
|
||||
// ✅ 修复: 并行执行
|
||||
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Readability (可读性)
|
||||
|
||||
### 检查清单
|
||||
|
||||
- [ ] **命名规范**
|
||||
- 变量名见名知意
|
||||
- 函数名表达动作
|
||||
- 常量使用 UPPER_CASE
|
||||
- 避免缩写和单字母
|
||||
|
||||
- [ ] **函数设计**
|
||||
- 单一职责
|
||||
- 长度 < 50 行
|
||||
- 参数 < 5 个
|
||||
- 嵌套 < 4 层
|
||||
|
||||
- [ ] **代码组织**
|
||||
- 逻辑分组
|
||||
- 空行分隔
|
||||
- Import 顺序
|
||||
|
||||
- [ ] **注释质量**
|
||||
- 解释 WHY 而非 WHAT
|
||||
- 及时更新
|
||||
- 无冗余注释
|
||||
|
||||
### 常见问题模式
|
||||
|
||||
```javascript
|
||||
// ❌ 问题: 命名不清晰
|
||||
const d = new Date();
|
||||
const a = users.filter(x => x.s === 'active');
|
||||
|
||||
// ✅ 修复
|
||||
const currentDate = new Date();
|
||||
const activeUsers = users.filter(user => user.status === 'active');
|
||||
|
||||
// ❌ 问题: 函数过长、职责过多
|
||||
function processOrder(order) {
|
||||
// ... 200 行代码,包含验证、计算、保存、通知
|
||||
}
|
||||
|
||||
// ✅ 修复: 拆分职责
|
||||
function validateOrder(order) { /* ... */ }
|
||||
function calculateTotal(order) { /* ... */ }
|
||||
function saveOrder(order) { /* ... */ }
|
||||
function notifyCustomer(order) { /* ... */ }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Testing (测试)
|
||||
|
||||
### 检查清单
|
||||
|
||||
- [ ] **测试覆盖**
|
||||
- 核心逻辑有测试
|
||||
- 边界条件有测试
|
||||
- 错误路径有测试
|
||||
|
||||
- [ ] **测试质量**
|
||||
- 测试独立
|
||||
- 断言明确
|
||||
- Mock 适度
|
||||
|
||||
- [ ] **测试可维护性**
|
||||
- 命名清晰
|
||||
- 结构统一
|
||||
- 避免重复
|
||||
|
||||
### 常见问题模式
|
||||
|
||||
```javascript
|
||||
// ❌ 问题: 测试不独立
|
||||
let counter = 0;
|
||||
test('increment', () => {
|
||||
counter++; // 依赖外部状态
|
||||
expect(counter).toBe(1);
|
||||
});
|
||||
|
||||
// ✅ 修复: 每个测试独立
|
||||
test('increment', () => {
|
||||
const counter = new Counter();
|
||||
counter.increment();
|
||||
expect(counter.value).toBe(1);
|
||||
});
|
||||
|
||||
// ❌ 问题: 缺少边界测试
|
||||
test('divide', () => {
|
||||
expect(divide(10, 2)).toBe(5);
|
||||
});
|
||||
|
||||
// ✅ 修复: 包含边界情况
|
||||
test('divide by zero throws', () => {
|
||||
expect(() => divide(10, 0)).toThrow();
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Architecture (架构)
|
||||
|
||||
### 检查清单
|
||||
|
||||
- [ ] **分层结构**
|
||||
- 层次清晰
|
||||
- 依赖方向正确
|
||||
- 无循环依赖
|
||||
|
||||
- [ ] **模块化**
|
||||
- 高内聚低耦合
|
||||
- 接口定义清晰
|
||||
- 职责单一
|
||||
|
||||
- [ ] **设计模式**
|
||||
- 使用合适的模式
|
||||
- 避免过度设计
|
||||
- 遵循项目既有模式
|
||||
|
||||
### 常见问题模式
|
||||
|
||||
```javascript
|
||||
// ❌ 问题: 层次混乱 (Controller 直接操作数据库)
|
||||
class UserController {
|
||||
async getUser(req, res) {
|
||||
const user = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
|
||||
res.json(user);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 修复: 分层清晰
|
||||
class UserController {
|
||||
constructor(private userService: UserService) {}
|
||||
|
||||
async getUser(req, res) {
|
||||
const user = await this.userService.findById(req.params.id);
|
||||
res.json(user);
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 问题: 循环依赖
|
||||
// moduleA.ts
|
||||
import { funcB } from './moduleB';
|
||||
// moduleB.ts
|
||||
import { funcA } from './moduleA';
|
||||
|
||||
// ✅ 修复: 提取共享模块或使用依赖注入
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Severity Mapping
|
||||
|
||||
| Severity | Criteria |
|
||||
|----------|----------|
|
||||
| **Critical** | 安全漏洞、数据损坏风险、崩溃风险 |
|
||||
| **High** | 功能缺陷、性能严重问题、重要边界未处理 |
|
||||
| **Medium** | 代码质量问题、可维护性问题 |
|
||||
| **Low** | 风格问题、优化建议 |
|
||||
| **Info** | 信息性建议、学习机会 |
|
||||
186
.claude/skills/review-code/templates/issue-template.md
Normal file
186
.claude/skills/review-code/templates/issue-template.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Issue Template
|
||||
|
||||
问题记录模板。
|
||||
|
||||
## Single Issue Template
|
||||
|
||||
```markdown
|
||||
#### {{severity_emoji}} [{{id}}] {{category}}
|
||||
|
||||
- **严重程度**: {{severity}}
|
||||
- **维度**: {{dimension}}
|
||||
- **文件**: `{{file}}`{{#if line}}:{{line}}{{/if}}
|
||||
- **描述**: {{description}}
|
||||
|
||||
{{#if code_snippet}}
|
||||
**问题代码**:
|
||||
```{{language}}
|
||||
{{code_snippet}}
|
||||
```
|
||||
{{/if}}
|
||||
|
||||
**建议**: {{recommendation}}
|
||||
|
||||
{{#if fix_example}}
|
||||
**修复示例**:
|
||||
```{{language}}
|
||||
{{fix_example}}
|
||||
```
|
||||
{{/if}}
|
||||
|
||||
{{#if references}}
|
||||
**参考资料**:
|
||||
{{#each references}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
```
|
||||
|
||||
## Issue Object Schema
|
||||
|
||||
```typescript
|
||||
interface Issue {
|
||||
id: string; // e.g., "SEC-001"
|
||||
severity: 'critical' | 'high' | 'medium' | 'low' | 'info';
|
||||
dimension: string; // e.g., "security"
|
||||
category: string; // e.g., "xss-risk"
|
||||
file: string; // e.g., "src/utils/render.ts"
|
||||
line?: number; // e.g., 42
|
||||
column?: number; // e.g., 15
|
||||
code_snippet?: string;
|
||||
description: string;
|
||||
recommendation: string;
|
||||
fix_example?: string;
|
||||
references?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
## ID Generation
|
||||
|
||||
```javascript
|
||||
function generateIssueId(dimension, counter) {
|
||||
const prefixes = {
|
||||
correctness: 'CORR',
|
||||
readability: 'READ',
|
||||
performance: 'PERF',
|
||||
security: 'SEC',
|
||||
testing: 'TEST',
|
||||
architecture: 'ARCH'
|
||||
};
|
||||
|
||||
const prefix = prefixes[dimension] || 'MISC';
|
||||
const number = String(counter).padStart(3, '0');
|
||||
|
||||
return `${prefix}-${number}`;
|
||||
}
|
||||
```
|
||||
|
||||
## Severity Emojis
|
||||
|
||||
```javascript
|
||||
const SEVERITY_EMOJI = {
|
||||
critical: '🔴',
|
||||
high: '🟠',
|
||||
medium: '🟡',
|
||||
low: '🔵',
|
||||
info: '⚪'
|
||||
};
|
||||
```
|
||||
|
||||
## Issue Categories by Dimension
|
||||
|
||||
### Correctness
|
||||
- `null-check` - 缺少空值检查
|
||||
- `boundary` - 边界条件未处理
|
||||
- `error-handling` - 错误处理不当
|
||||
- `type-safety` - 类型安全问题
|
||||
- `logic-error` - 逻辑错误
|
||||
- `resource-leak` - 资源泄漏
|
||||
|
||||
### Security
|
||||
- `injection` - 注入风险
|
||||
- `xss` - 跨站脚本
|
||||
- `hardcoded-secret` - 硬编码密钥
|
||||
- `auth` - 认证授权
|
||||
- `sensitive-data` - 敏感数据
|
||||
|
||||
### Performance
|
||||
- `complexity` - 复杂度问题
|
||||
- `n+1-query` - N+1 查询
|
||||
- `memory-leak` - 内存泄漏
|
||||
- `blocking-io` - 阻塞 I/O
|
||||
- `inefficient-algorithm` - 低效算法
|
||||
|
||||
### Readability
|
||||
- `naming` - 命名问题
|
||||
- `function-length` - 函数过长
|
||||
- `nesting-depth` - 嵌套过深
|
||||
- `comments` - 注释问题
|
||||
- `duplication` - 代码重复
|
||||
|
||||
### Testing
|
||||
- `coverage` - 覆盖不足
|
||||
- `boundary-test` - 缺少边界测试
|
||||
- `test-isolation` - 测试不独立
|
||||
- `flaky-test` - 不稳定测试
|
||||
|
||||
### Architecture
|
||||
- `layer-violation` - 层次违规
|
||||
- `circular-dependency` - 循环依赖
|
||||
- `coupling` - 耦合过紧
|
||||
- `srp-violation` - 单一职责违规
|
||||
|
||||
## Example Issues
|
||||
|
||||
### Critical Security Issue
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "SEC-001",
|
||||
"severity": "critical",
|
||||
"dimension": "security",
|
||||
"category": "xss",
|
||||
"file": "src/components/Comment.tsx",
|
||||
"line": 25,
|
||||
"code_snippet": "element.innerHTML = userComment;",
|
||||
"description": "直接使用 innerHTML 插入用户输入,存在 XSS 攻击风险",
|
||||
"recommendation": "使用 textContent 或对用户输入进行 HTML 转义",
|
||||
"fix_example": "element.textContent = userComment;\n// 或\nelement.innerHTML = DOMPurify.sanitize(userComment);",
|
||||
"references": [
|
||||
"https://owasp.org/www-community/xss-filter-evasion-cheatsheet"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### High Correctness Issue
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "CORR-003",
|
||||
"severity": "high",
|
||||
"dimension": "correctness",
|
||||
"category": "error-handling",
|
||||
"file": "src/services/api.ts",
|
||||
"line": 42,
|
||||
"code_snippet": "try {\n await fetchData();\n} catch (e) {}",
|
||||
"description": "空的 catch 块会静默吞掉错误,导致问题难以发现和调试",
|
||||
"recommendation": "记录错误日志或重新抛出异常",
|
||||
"fix_example": "try {\n await fetchData();\n} catch (e) {\n console.error('Failed to fetch data:', e);\n throw e;\n}"
|
||||
}
|
||||
```
|
||||
|
||||
### Medium Readability Issue
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "READ-007",
|
||||
"severity": "medium",
|
||||
"dimension": "readability",
|
||||
"category": "function-length",
|
||||
"file": "src/utils/processor.ts",
|
||||
"line": 15,
|
||||
"description": "函数 processData 有 150 行,超过推荐的 50 行限制,难以理解和维护",
|
||||
"recommendation": "将函数拆分为多个小函数,每个函数负责单一职责",
|
||||
"fix_example": "// 拆分为:\nfunction validateInput(data) { ... }\nfunction transformData(data) { ... }\nfunction saveData(data) { ... }"
|
||||
}
|
||||
```
|
||||
173
.claude/skills/review-code/templates/review-report.md
Normal file
173
.claude/skills/review-code/templates/review-report.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Review Report Template
|
||||
|
||||
审查报告模板。
|
||||
|
||||
## Template Structure
|
||||
|
||||
```markdown
|
||||
# Code Review Report
|
||||
|
||||
## 审查概览
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|------|
|
||||
| 目标路径 | `{{target_path}}` |
|
||||
| 文件数量 | {{file_count}} |
|
||||
| 代码行数 | {{total_lines}} |
|
||||
| 主要语言 | {{language}} |
|
||||
| 框架 | {{framework}} |
|
||||
| 审查时间 | {{review_duration}} |
|
||||
|
||||
## 问题统计
|
||||
|
||||
| 严重程度 | 数量 |
|
||||
|----------|------|
|
||||
| 🔴 Critical | {{critical_count}} |
|
||||
| 🟠 High | {{high_count}} |
|
||||
| 🟡 Medium | {{medium_count}} |
|
||||
| 🔵 Low | {{low_count}} |
|
||||
| ⚪ Info | {{info_count}} |
|
||||
| **总计** | **{{total_issues}}** |
|
||||
|
||||
### 按维度统计
|
||||
|
||||
| 维度 | 问题数 |
|
||||
|------|--------|
|
||||
| Correctness (正确性) | {{correctness_count}} |
|
||||
| Security (安全性) | {{security_count}} |
|
||||
| Performance (性能) | {{performance_count}} |
|
||||
| Readability (可读性) | {{readability_count}} |
|
||||
| Testing (测试) | {{testing_count}} |
|
||||
| Architecture (架构) | {{architecture_count}} |
|
||||
|
||||
---
|
||||
|
||||
## 高风险区域
|
||||
|
||||
{{#if risk_areas}}
|
||||
| 文件 | 原因 | 优先级 |
|
||||
|------|------|--------|
|
||||
{{#each risk_areas}}
|
||||
| `{{this.file}}` | {{this.reason}} | {{this.priority}} |
|
||||
{{/each}}
|
||||
{{else}}
|
||||
未发现明显的高风险区域。
|
||||
{{/if}}
|
||||
|
||||
---
|
||||
|
||||
## 问题详情
|
||||
|
||||
{{#each dimensions}}
|
||||
### {{this.name}}
|
||||
|
||||
{{#each this.findings}}
|
||||
#### {{severity_emoji this.severity}} [{{this.id}}] {{this.category}}
|
||||
|
||||
- **严重程度**: {{this.severity}}
|
||||
- **文件**: `{{this.file}}`{{#if this.line}}:{{this.line}}{{/if}}
|
||||
- **描述**: {{this.description}}
|
||||
|
||||
{{#if this.code_snippet}}
|
||||
```
|
||||
{{this.code_snippet}}
|
||||
```
|
||||
{{/if}}
|
||||
|
||||
**建议**: {{this.recommendation}}
|
||||
|
||||
{{#if this.fix_example}}
|
||||
**修复示例**:
|
||||
```
|
||||
{{this.fix_example}}
|
||||
```
|
||||
{{/if}}
|
||||
|
||||
---
|
||||
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
|
||||
## 审查建议
|
||||
|
||||
### 必须修复 (Must Fix)
|
||||
|
||||
{{must_fix_summary}}
|
||||
|
||||
### 建议改进 (Should Fix)
|
||||
|
||||
{{should_fix_summary}}
|
||||
|
||||
### 可选优化 (Nice to Have)
|
||||
|
||||
{{nice_to_have_summary}}
|
||||
|
||||
---
|
||||
|
||||
*报告生成时间: {{generated_at}}*
|
||||
```
|
||||
|
||||
## Variable Definitions
|
||||
|
||||
| Variable | Type | Source |
|
||||
|----------|------|--------|
|
||||
| `{{target_path}}` | string | state.context.target_path |
|
||||
| `{{file_count}}` | number | state.context.file_count |
|
||||
| `{{total_lines}}` | number | state.context.total_lines |
|
||||
| `{{language}}` | string | state.context.language |
|
||||
| `{{framework}}` | string | state.context.framework |
|
||||
| `{{review_duration}}` | string | Formatted duration |
|
||||
| `{{critical_count}}` | number | Count of critical findings |
|
||||
| `{{high_count}}` | number | Count of high findings |
|
||||
| `{{medium_count}}` | number | Count of medium findings |
|
||||
| `{{low_count}}` | number | Count of low findings |
|
||||
| `{{info_count}}` | number | Count of info findings |
|
||||
| `{{total_issues}}` | number | Total findings |
|
||||
| `{{risk_areas}}` | array | state.scan_summary.risk_areas |
|
||||
| `{{dimensions}}` | array | Grouped findings by dimension |
|
||||
| `{{generated_at}}` | string | ISO timestamp |
|
||||
|
||||
## Helper Functions
|
||||
|
||||
```javascript
|
||||
function severity_emoji(severity) {
|
||||
const emojis = {
|
||||
critical: '🔴',
|
||||
high: '🟠',
|
||||
medium: '🟡',
|
||||
low: '🔵',
|
||||
info: '⚪'
|
||||
};
|
||||
return emojis[severity] || '⚪';
|
||||
}
|
||||
|
||||
function formatDuration(ms) {
|
||||
const minutes = Math.floor(ms / 60000);
|
||||
const seconds = Math.floor((ms % 60000) / 1000);
|
||||
return `${minutes}分${seconds}秒`;
|
||||
}
|
||||
|
||||
function generateMustFixSummary(findings) {
|
||||
const critical = findings.filter(f => f.severity === 'critical');
|
||||
const high = findings.filter(f => f.severity === 'high');
|
||||
|
||||
if (critical.length + high.length === 0) {
|
||||
return '未发现必须立即修复的问题。';
|
||||
}
|
||||
|
||||
return `发现 ${critical.length} 个严重问题和 ${high.length} 个高优先级问题,建议在合并前修复。`;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
```javascript
|
||||
const report = generateReport({
|
||||
context: state.context,
|
||||
summary: state.summary,
|
||||
findings: state.findings,
|
||||
scanSummary: state.scan_summary
|
||||
});
|
||||
|
||||
Write(`${workDir}/review-report.md`, report);
|
||||
```
|
||||
Reference in New Issue
Block a user