# 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 = []; // 从外部 JSON 文件加载规则 const rulesConfig = loadRulesConfig(dimension, workDir); const rules = rulesConfig.rules || []; const prefix = rulesConfig.prefix || getDimensionPrefix(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 = detectByPattern(content, lines, file, rule); for (const match of matches) { findings.push({ id: `${prefix}-${String(findingCounter++).padStart(3, '0')}`, severity: rule.severity || match.severity, dimension: dimension, category: rule.category, file: file, line: match.line, code_snippet: match.snippet, description: rule.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 } }; } /** * 从外部 JSON 文件加载规则配置 * 规则文件位于 specs/rules/{dimension}-rules.json * @param {string} dimension - 维度名称 (correctness, security, etc.) * @param {string} workDir - 工作目录 (用于日志记录) * @returns {object} 规则配置对象,包含 rules 数组和 prefix */ function loadRulesConfig(dimension, workDir) { // 规则文件路径:相对于 skill 目录 const rulesPath = `specs/rules/${dimension}-rules.json`; try { const rulesFile = Read(rulesPath); const rulesConfig = JSON.parse(rulesFile); return rulesConfig; } catch (e) { console.warn(`Failed to load rules for ${dimension}: ${e.message}`); // 返回空规则配置,保持向后兼容 return { rules: [], prefix: getDimensionPrefix(dimension) }; } } /** * 根据规则的 patternType 检测代码问题 * 支持的 patternType: regex, includes * @param {string} content - 文件内容 * @param {string[]} lines - 按行分割的内容 * @param {string} file - 文件路径 * @param {object} rule - 规则配置对象 * @returns {Array} 匹配结果数组 */ function detectByPattern(content, lines, file, rule) { const matches = []; const { pattern, patternType, negativePatterns, caseInsensitive } = rule; if (!pattern) return matches; switch (patternType) { case 'regex': return detectByRegex(content, lines, pattern, negativePatterns, caseInsensitive); case 'includes': return detectByIncludes(content, lines, pattern, negativePatterns); default: // 默认使用 includes 模式 return detectByIncludes(content, lines, pattern, negativePatterns); } } /** * 使用正则表达式检测代码问题 * @param {string} content - 文件完整内容 * @param {string[]} lines - 按行分割的内容 * @param {string} pattern - 正则表达式模式 * @param {string[]} negativePatterns - 排除模式列表 * @param {boolean} caseInsensitive - 是否忽略大小写 * @returns {Array} 匹配结果数组 */ function detectByRegex(content, lines, pattern, negativePatterns, caseInsensitive) { const matches = []; const flags = caseInsensitive ? 'gi' : 'g'; try { const regex = new RegExp(pattern, flags); let match; while ((match = regex.exec(content)) !== null) { const lineNum = content.substring(0, match.index).split('\n').length; const lineContent = lines[lineNum - 1] || ''; // 检查排除模式 - 如果行内容匹配任一排除模式则跳过 if (negativePatterns && negativePatterns.length > 0) { const shouldExclude = negativePatterns.some(np => { try { return new RegExp(np).test(lineContent); } catch { return lineContent.includes(np); } }); if (shouldExclude) continue; } matches.push({ line: lineNum, snippet: lineContent.trim().substring(0, 100), matchedText: match[0] }); } } catch (e) { console.warn(`Invalid regex pattern: ${pattern}`); } return matches; } /** * 使用字符串包含检测代码问题 * @param {string} content - 文件完整内容 (未使用但保持接口一致) * @param {string[]} lines - 按行分割的内容 * @param {string} pattern - 要查找的字符串 * @param {string[]} negativePatterns - 排除模式列表 * @returns {Array} 匹配结果数组 */ function detectByIncludes(content, lines, pattern, negativePatterns) { const matches = []; lines.forEach((line, i) => { if (line.includes(pattern)) { // 检查排除模式 - 如果行内容包含任一排除字符串则跳过 if (negativePatterns && negativePatterns.length > 0) { const shouldExclude = negativePatterns.some(np => line.includes(np)); if (shouldExclude) return; } matches.push({ line: i + 1, snippet: line.trim().substring(0, 100), matchedText: pattern }); } }); return matches; } /** * 获取维度前缀(作为规则文件不存在时的备用) * @param {string} dimension - 维度名称 * @returns {string} 4字符前缀 */ function getDimensionPrefix(dimension) { const prefixes = { correctness: 'CORR', readability: 'READ', performance: 'PERF', security: 'SEC', testing: 'TEST', architecture: 'ARCH' }; return prefixes[dimension] || 'MISC'; } ``` ## 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