Code Quality Examples

Validation rules to enforce code quality standards.

No Console Statements

Prevents console.log and similar statements in production code.

import { defineCodeRule } from "@syncrolabs/claude-code-validator";

export const noConsole = defineCodeRule({
  name: "no-console",
  description: "Prevents console statements in production code",

  shouldRun: (context) => {
    // Only check source files, not test files
    return (
      /\.(ts|js|vue)$/.test(context.filePath) &&
      !/\.(test|spec)\.(ts|js)$/.test(context.filePath)
    );
  },

  validate(context) {
    const errors: string[] = [];

    const consolePatterns = [
      { regex: /console\.log\(/, method: "console.log" },
      { regex: /console\.debug\(/, method: "console.debug" },
      { regex: /console\.info\(/, method: "console.info" },
      { regex: /console\.warn\(/, method: "console.warn" },
    ];

    for (const { regex, method } of consolePatterns) {
      if (regex.test(context.content)) {
        errors.push(
          `❌ ${method}() detected in production code\n` +
            `   → Use a proper logging library instead\n` +
            `   â„šī¸  Consider using: pino, winston, or consola\n` +
            `   📄 File: ${context.filePath}`
        );
      }
    }

    return errors;
  },
});

No TODO Comments

Ensures TODO comments aren't committed.

import { defineCodeRule } from "@syncrolabs/claude-code-validator";

export const noTodos = defineCodeRule({
  name: "no-todos",
  description: "Prevents TODO comments in committed code",

  shouldRun: () => true,

  validate(context) {
    const errors: string[] = [];

    // Match TODO, FIXME, HACK, XXX comments
    const todoPattern = /(TODO|FIXME|HACK|XXX):/i;

    if (todoPattern.test(context.content)) {
      errors.push(
        `❌ TODO/FIXME comment detected\n` +
          `   → Complete the task or create an issue\n` +
          `   â„šī¸  Remove temporary comments before committing\n` +
          `   📄 File: ${context.filePath}`
      );
    }

    return errors;
  },
});

Enforce Async/Await

Prefers async/await over .then() chains.

import { defineCodeRule } from "@syncrolabs/claude-code-validator";

export const preferAsyncAwait = defineCodeRule({
  name: "prefer-async-await",
  description: "Prefers async/await over Promise chains",

  shouldRun: (context) => /\.(ts|js)$/.test(context.filePath),

  validate(context) {
    const errors: string[] = [];

    // Check for .then() chains
    const thenPattern = /\.\s*then\s*\(/g;
    const matches = context.content.match(thenPattern);

    if (matches && matches.length > 0) {
      errors.push(
        `❌ Promise .then() chain detected\n` +
          `   → Use async/await for better readability\n` +
          `   â„šī¸  Example:\n` +
          `      // Instead of: promise.then(result => ...)\n` +
          `      // Use: const result = await promise\n` +
          `   📄 File: ${context.filePath}`
      );
    }

    return errors;
  },
});

No Magic Numbers

Requires constants for magic numbers.

import { defineCodeRule } from "@syncrolabs/claude-code-validator";

export const noMagicNumbers = defineCodeRule({
  name: "no-magic-numbers",
  description: "Prevents magic numbers without constants",

  shouldRun: (context) => /\.(ts|js)$/.test(context.filePath),

  validate(context) {
    const errors: string[] = [];

    // Look for suspicious numeric literals (not 0, 1, -1, 2)
    // This is a simplified version - a real implementation would be more sophisticated
    const magicNumberPattern = /\b(?!0|1|2|-1)\d{3,}\b/g;
    const matches = context.content.match(magicNumberPattern);

    if (matches && matches.length > 0) {
      const uniqueNumbers = [...new Set(matches)];

      errors.push(
        `❌ Magic numbers detected: ${uniqueNumbers.join(", ")}\n` +
          `   → Extract to named constants\n` +
          `   â„šī¸  Example:\n` +
          `      const MAX_RETRY_ATTEMPTS = 3;\n` +
          `      const TIMEOUT_MS = 5000;\n` +
          `   📄 File: ${context.filePath}`
      );
    }

    return errors;
  },
});

Enforce Named Exports

Prefers named exports over default exports.

import { defineCodeRule } from "@syncrolabs/claude-code-validator";

export const preferNamedExports = defineCodeRule({
  name: "prefer-named-exports",
  description: "Prefers named exports over default exports",

  shouldRun: (context) => {
    // Skip Vue SFCs and certain files
    return (
      /\.(ts|js)$/.test(context.filePath) &&
      !context.filePath.includes("index.")
    );
  },

  validate(context) {
    const errors: string[] = [];

    // Check for default exports
    if (/export\s+default/.test(context.content)) {
      errors.push(
        `❌ Default export detected\n` +
          `   → Use named exports for better refactoring\n` +
          `   â„šī¸  Named exports:\n` +
          `      - Enable better tree-shaking\n` +
          `      - Improve IDE auto-imports\n` +
          `      - Make refactoring easier\n` +
          `   📄 File: ${context.filePath}`
      );
    }

    return errors;
  },
});

No Any Type

Prevents usage of any type in TypeScript.

import { defineCodeRule } from "@syncrolabs/claude-code-validator";

export const noAnyType = defineCodeRule({
  name: "no-any-type",
  description: "Prevents usage of any type",

  shouldRun: (context) => /\.ts$/.test(context.filePath),

  validate(context) {
    const errors: string[] = [];

    // Check for : any or <any> or as any
    const anyTypePatterns = [/:\s*any\b/, /<any>/, /as\s+any\b/];

    for (const pattern of anyTypePatterns) {
      if (pattern.test(context.content)) {
        errors.push(
          `❌ Type 'any' detected\n` +
            `   → Use proper types or 'unknown' for dynamic values\n` +
            `   â„šī¸  Consider:\n` +
            `      - Define a proper interface\n` +
            `      - Use 'unknown' and type guards\n` +
            `      - Use generics for flexible types\n` +
            `   📄 File: ${context.filePath}`
        );
        break;
      }
    }

    return errors;
  },
});

Example Usage

Combine all code quality rules:

// .claude/rules/code-quality.ts
import { defineCodeRule } from "@syncrolabs/claude-code-validator";

export const noConsole = defineCodeRule({
  /* ... */
});
export const noTodos = defineCodeRule({
  /* ... */
});
export const preferAsyncAwait = defineCodeRule({
  /* ... */
});
export const noMagicNumbers = defineCodeRule({
  /* ... */
});
export const preferNamedExports = defineCodeRule({
  /* ... */
});
export const noAnyType = defineCodeRule({
  /* ... */
});

All rules will be automatically discovered and enforced!

Customization

You can customize these rules for your team's needs:

// Adjust strictness
export const noConsole = defineCodeRule({
  name: "no-console",
  description: "Prevents console statements",

  shouldRun: (context) => {
    // Allow console in development files
    return (
      !context.filePath.includes("dev/") && !context.filePath.includes(".dev.")
    );
  },

  validate(context) {
    // Only block console.log, allow console.error
    if (/console\.log\(/.test(context.content)) {
      return ["❌ console.log() not allowed in production"];
    }
    return [];
  },
});