Code Style Conventions Observed in OpenClaw

This document captures observations about OpenClaw's code formatting, linting, and TypeScript configuration based on analysis of the codebase. These conventions ensure consistency and pass CI checks.

Linter: Oxlint (NOT ESLint)

OpenClaw uses Oxlint, not ESLint. Configuration is in .oxlintrc.json.

Key Configuration

{
  "categories": {
    "correctness": "error",
    "perf": "error",
    "suspicious": "error"
  },
  "rules": {
    "typescript/no-explicit-any": "error",
    "curly": "error"
  }
}

Critical Rules

  • typescript/no-explicit-any: "error" - Never use any type (strict enforcement)
  • curly: "error" - Always use braces for control flow statements
  • Categories as errors - Correctness, performance, and suspicious patterns are all errors, not warnings

Formatter: Oxfmt (NOT Prettier)

OpenClaw uses Oxfmt, not Prettier. Configuration is in .oxfmtrc.jsonc.

Key Configuration

{
  "indent_width": 2,
  "use_tabs": false,
  "experimental": {
    "sort_imports": true
  }
}

Formatting Rules

  • 2-space indentation, no tabs
  • Experimental sort imports enabled - Import statements are automatically sorted
  • Oxfmt handles all formatting; no need to run Prettier

TypeScript Configuration

TypeScript config is in tsconfig.json with strict mode enabled.

Key Settings

{
  "compilerOptions": {
    "strict": true,
    "target": "ES2023",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "experimentalDecorators": true,
    "noEmit": true
  }
}

Notable Settings

  • strict: true - All strict type-checking options enabled
  • experimentalDecorators: true - Required for legacy decorators in Control UI Lit components
  • noEmit: true - TypeScript is used for type checking only, not compilation
  • ES2023 target - Modern JavaScript features available
  • NodeNext module resolution - Supports ESM with .js extensions in imports

Type Definition Preferences

Prefer type Over interface

For object shapes, the codebase prefers type over interface.

Pattern observed:

/**
 * Context for gateway operations.
 */
export type GatewayContext = {
  tenantId: string;
  requestId: string;
  timestamp: number;
};

Not:

export interface GatewayContext {
  tenantId: string;
  requestId: string;
  timestamp: number;
}

Always Include JSDoc

Public types and functions should have JSDoc comments explaining their purpose.

Critical Rules from Contributing Docs

These rules are explicitly documented in CONTRIBUTING.md and CLAUDE.md:

Never Disable Type Safety

  • Never use @ts-nocheck - Fix type errors instead of suppressing them
  • Never disable no-explicit-any - Always provide explicit types
  • If you can't type something properly, ask for guidance rather than using any

Never Use Prototype Mutation

  • Never mutate prototypes for behavior sharing
  • Use extends instead - Proper class inheritance or composition

Bad:

SomeClass.prototype.newMethod = function() { ... };

Good:

class ExtendedClass extends SomeClass {
  newMethod() { ... }
}

File Size Target

  • Target: ~500-700 lines of code per file
  • Split larger files into focused modules
  • Exceptions for complex modules (with justification)

Product Naming

  • Product name: "OpenClaw" - Used in user-facing messages and documentation
  • CLI/config/paths: "openclaw" - Used in commands, config files, and directory names

Examples:

// User message
console.log("Welcome to OpenClaw!");

// Config file path
const configPath = path.join(homeDir, ".openclaw", "config.json");

Error Handling Patterns

Use formatErrorMessage() with Automatic Redaction

The codebase uses a centralized error formatting function that automatically redacts sensitive information.

Pattern observed:

import { formatErrorMessage, redactSensitiveText } from "./utils/error-utils.js";

try {
  // operation
} catch (error) {
  const message = formatErrorMessage(error, "Operation failed");
  logger.error(message);
}

Automatic Redaction

formatErrorMessage() automatically calls redactSensitiveText() to remove:

  • API keys
  • Tokens
  • Passwords
  • File paths (partially redacted)

Barrel Exports

Selective, Not Universal

The codebase uses barrel exports (index.ts) selectively:

  • Some modules have index.ts - Re-exporting public APIs
  • Some modules don't - Direct imports from specific files

Not a universal pattern - Check existing module structure before adding barrel exports.

Running Linting and Formatting

# Run Oxlint
pnpm lint

# Run Oxfmt (format check)
pnpm format:check

# Run Oxfmt (format write)
pnpm format

# Run all checks (includes TypeScript type checking)
pnpm check

Pre-Commit Hooks

Pre-commit hooks (installed via prek install) automatically run Oxlint and Oxfmt before commits. See PR and Commit Conventions for details.

Why This Matters

Following these conventions ensures:

  1. CI passes - Linting and formatting checks are part of CI
  2. Consistency - Code looks the same across the entire codebase
  3. Type safety - Strict TypeScript catches bugs early
  4. Security - Automatic redaction prevents sensitive data leaks
  5. Reviewability - Consistent style makes code reviews easier

Cross-References