From 29c8bb7a660c9c07d47494392f4ded300e4221b6 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Tue, 13 Jan 2026 14:39:16 +0800 Subject: [PATCH] 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. --- .claude/skills/review-code/SKILL.md | 170 +++++++++ .../phases/actions/action-collect-context.md | 139 ++++++++ .../phases/actions/action-complete.md | 115 ++++++ .../phases/actions/action-deep-review.md | 256 +++++++++++++ .../phases/actions/action-generate-report.md | 263 ++++++++++++++ .../phases/actions/action-quick-scan.md | 164 +++++++++ .../skills/review-code/phases/orchestrator.md | 192 ++++++++++ .../skills/review-code/phases/state-schema.md | 174 +++++++++ .../review-code/specs/issue-classification.md | 228 ++++++++++++ .../review-code/specs/quality-standards.md | 214 +++++++++++ .../review-code/specs/review-dimensions.md | 337 ++++++++++++++++++ .../review-code/templates/issue-template.md | 186 ++++++++++ .../review-code/templates/review-report.md | 173 +++++++++ 13 files changed, 2611 insertions(+) create mode 100644 .claude/skills/review-code/SKILL.md create mode 100644 .claude/skills/review-code/phases/actions/action-collect-context.md create mode 100644 .claude/skills/review-code/phases/actions/action-complete.md create mode 100644 .claude/skills/review-code/phases/actions/action-deep-review.md create mode 100644 .claude/skills/review-code/phases/actions/action-generate-report.md create mode 100644 .claude/skills/review-code/phases/actions/action-quick-scan.md create mode 100644 .claude/skills/review-code/phases/orchestrator.md create mode 100644 .claude/skills/review-code/phases/state-schema.md create mode 100644 .claude/skills/review-code/specs/issue-classification.md create mode 100644 .claude/skills/review-code/specs/quality-standards.md create mode 100644 .claude/skills/review-code/specs/review-dimensions.md create mode 100644 .claude/skills/review-code/templates/issue-template.md create mode 100644 .claude/skills/review-code/templates/review-report.md diff --git a/.claude/skills/review-code/SKILL.md b/.claude/skills/review-code/SKILL.md new file mode 100644 index 00000000..33bdef70 --- /dev/null +++ b/.claude/skills/review-code/SKILL.md @@ -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) | 问题模板 | diff --git a/.claude/skills/review-code/phases/actions/action-collect-context.md b/.claude/skills/review-code/phases/actions/action-collect-context.md new file mode 100644 index 00000000..23a6237b --- /dev/null +++ b/.claude/skills/review-code/phases/actions/action-collect-context.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 diff --git a/.claude/skills/review-code/phases/actions/action-complete.md b/.claude/skills/review-code/phases/actions/action-complete.md new file mode 100644 index 00000000..e79e0139 --- /dev/null +++ b/.claude/skills/review-code/phases/actions/action-complete.md @@ -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. 导出报告到其他位置 diff --git a/.claude/skills/review-code/phases/actions/action-deep-review.md b/.claude/skills/review-code/phases/actions/action-deep-review.md new file mode 100644 index 00000000..0691988f --- /dev/null +++ b/.claude/skills/review-code/phases/actions/action-deep-review.md @@ -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 diff --git a/.claude/skills/review-code/phases/actions/action-generate-report.md b/.claude/skills/review-code/phases/actions/action-generate-report.md new file mode 100644 index 00000000..fe73a8ee --- /dev/null +++ b/.claude/skills/review-code/phases/actions/action-generate-report.md @@ -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 diff --git a/.claude/skills/review-code/phases/actions/action-quick-scan.md b/.claude/skills/review-code/phases/actions/action-quick-scan.md new file mode 100644 index 00000000..81bbf192 --- /dev/null +++ b/.claude/skills/review-code/phases/actions/action-quick-scan.md @@ -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): 可询问用户是否缩小范围 diff --git a/.claude/skills/review-code/phases/orchestrator.md b/.claude/skills/review-code/phases/orchestrator.md new file mode 100644 index 00000000..8041661b --- /dev/null +++ b/.claude/skills/review-code/phases/orchestrator.md @@ -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 次 | +| 状态不一致 | 重新初始化状态 | +| 用户中止 | 保存当前进度,允许恢复 | diff --git a/.claude/skills/review-code/phases/state-schema.md b/.claude/skills/review-code/phases/state-schema.md new file mode 100644 index 00000000..f7884715 --- /dev/null +++ b/.claude/skills/review-code/phases/state-schema.md @@ -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 +``` diff --git a/.claude/skills/review-code/specs/issue-classification.md b/.claude/skills/review-code/specs/issue-classification.md new file mode 100644 index 00000000..2fd540c5 --- /dev/null +++ b/.claude/skills/review-code/specs/issue-classification.md @@ -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 | diff --git a/.claude/skills/review-code/specs/quality-standards.md b/.claude/skills/review-code/specs/quality-standards.md new file mode 100644 index 00000000..047175fc --- /dev/null +++ b/.claude/skills/review-code/specs/quality-standards.md @@ -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 格式 +- 标准化严重程度判定 +- 使用模板化描述 diff --git a/.claude/skills/review-code/specs/review-dimensions.md b/.claude/skills/review-code/specs/review-dimensions.md new file mode 100644 index 00000000..36762254 --- /dev/null +++ b/.claude/skills/review-code/specs/review-dimensions.md @@ -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** | 信息性建议、学习机会 | diff --git a/.claude/skills/review-code/templates/issue-template.md b/.claude/skills/review-code/templates/issue-template.md new file mode 100644 index 00000000..2cbae2be --- /dev/null +++ b/.claude/skills/review-code/templates/issue-template.md @@ -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) { ... }" +} +``` diff --git a/.claude/skills/review-code/templates/review-report.md b/.claude/skills/review-code/templates/review-report.md new file mode 100644 index 00000000..9ce9451d --- /dev/null +++ b/.claude/skills/review-code/templates/review-report.md @@ -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); +```