Files
Claude-Code-Workflow/.claude/skills/review-code/phases/actions/action-deep-review.md
catlog22 15514c8f91 Add multi-dimensional code review rules for architecture, correctness, performance, readability, security, and testing
- Introduced architecture rules to detect circular dependencies, god classes, layer violations, and mixed concerns.
- Added correctness rules focusing on null checks, empty catch blocks, unreachable code, and type coercion.
- Implemented performance rules addressing nested loops, synchronous I/O, memory leaks, and unnecessary re-renders in React.
- Created readability rules to improve function length, variable naming, deep nesting, magic numbers, and commented code.
- Established security rules to identify XSS risks, hardcoded secrets, SQL injection vulnerabilities, and insecure random generation.
- Developed testing rules to enhance test quality, coverage, and maintainability, including missing assertions and error path testing.
- Documented the structure and schema for rule files in the index.md for better understanding and usage.
2026-01-13 14:53:20 +08:00

7.9 KiB

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

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

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