repo_description: The missing DevTools for Claude Code — inspect session logs, tool calls, token usage, subagents, and context window in a visual UI. Free, open source.
repo_topics: ai ai-agent ai-debugging ai-tools anthropic claude claude-code claude-code-tools debugging desktop-app developer-tools devtools electron llm macos-app observability open-source session-viewer token-usage typescript
code: import { defineConfig, globalIgnores } from 'eslint/config';
  import js from '@eslint/js';
  import tseslint from 'typescript-eslint';
  import reactPlugin from 'eslint-plugin-react';
  import reactHooks from 'eslint-plugin-react-hooks';
  import reactRefresh from 'eslint-plugin-react-refresh';
  import jsxA11y from 'eslint-plugin-jsx-a11y';
  import tailwindcss from 'eslint-plugin-tailwindcss';
  import sonarjs from 'eslint-plugin-sonarjs';
  import simpleImportSort from 'eslint-plugin-simple-import-sort';
  import importPlugin from 'eslint-plugin-import';
  import security from 'eslint-plugin-security';
  import boundaries from 'eslint-plugin-boundaries';
  import eslintComments from '@eslint-community/eslint-plugin-eslint-comments';
  import eslintConfigPrettier from 'eslint-config-prettier/flat';
  import globals from 'globals';
  
  export default defineConfig([
    // Global ignores
    globalIgnores([
      'dist/**',
      'dist-electron/**',
      'build/**',
      'node_modules/**',
      '*.config.js',
      '*.config.cjs',
      '*.config.ts',
      'out/**',
    ]),
  
    // Base ESLint recommended rules
    js.configs.recommended,
  
    // TypeScript-ESLint recommended with type checking + stylistic
    // Using recommended (not strict) for a balanced approach
    ...tseslint.configs.recommendedTypeChecked,
    ...tseslint.configs.stylisticTypeChecked,
  
    // SonarJS - Code quality and bug detection rules
    sonarjs.configs.recommended,
  
    // Security - Catch common security mistakes in AI-generated code
    security.configs.recommended,
  
    // TypeScript parser options for type-aware linting
    {
      name: 'typescript-parser-options',
      languageOptions: {
        parserOptions: {
          projectService: true,
          tsconfigRootDir: import.meta.dirname,
        },
      },
    },
  
    // Import plugin configuration - Main/Preload (uses tsconfig.node.json)
    {
      name: 'import-plugin-main',
      files: ['src/main/**/*.ts', 'src/preload/**/*.ts'],
      plugins: {
        import: importPlugin,
      },
      settings: {
        'import/resolver': {
          typescript: {
            alwaysTryTypes: true,
            project: './tsconfig.node.json',
          },
        },
      },
      rules: {
        'import/no-cycle': ['error', { maxDepth: 3, ignoreExternal: true }],
        'import/no-unresolved': 'error',
        'import/no-default-export': 'warn',
      },
    },
  
    // Import plugin configuration - Renderer (uses tsconfig.json)
    {
      name: 'import-plugin-renderer',
      files: ['src/renderer/**/*.{ts,tsx}'],
      plugins: {
        import: importPlugin,
      },
      settings: {
        'import/resolver': {
          typescript: {
            alwaysTryTypes: true,
            project: './tsconfig.json',
          },
        },
      },
      rules: {
        'import/no-cycle': ['error', { maxDepth: 3, ignoreExternal: true }],
        'import/no-unresolved': 'error',
        'import/no-default-export': 'warn',
      },
    },
  
    // Module boundaries - Enforce Electron three-process architecture
    {
      name: 'module-boundaries',
      files: ['src/**/*.{js,jsx,ts,tsx}'],
      plugins: {
        boundaries: boundaries,
      },
      settings: {
        'boundaries/elements': [
          { type: 'main', pattern: 'src/main/**', mode: 'folder' },
          { type: 'preload', pattern: 'src/preload/**', mode: 'folder' },
          { type: 'renderer', pattern: 'src/renderer/**', mode: 'folder' },
          { type: 'shared', pattern: 'src/shared/**', mode: 'folder' },
        ],
        'boundaries/ignore': ['**/*.test.ts', '**/*.spec.ts'],
      },
      rules: {
        // Enforce strict module boundaries for Electron architecture
        'boundaries/element-types': [
          'error',
          {
            default: 'disallow',
            rules: [
              // Renderer can only import from renderer and shared
              { from: 'renderer', allow: ['renderer', 'shared'] },
              // Main process can only import from main and shared
              { from: 'main', allow: ['main', 'shared'] },
              // Preload can only import from preload and shared
              { from: 'preload', allow: ['preload', 'shared'] },
              // Shared can import from shared and main (for type re-exports)
              { from: 'shared', allow: ['shared', 'main'] },
            ],
          },
        ],
        // Prevent importing private modules
        'boundaries/no-private': 'error',
      },
    },
  
    // ESLint Comments
    {
      name: 'eslint-comments',
      files: ['src/**/*.{js,jsx,ts,tsx}'],
      plugins: {
        '@eslint-community/eslint-comments': eslintComments,
      },
      rules: {
        // Prevents blanket-disabling rules
        '@eslint-community/eslint-comments/no-unlimited-disable': 'error',
        // Require description for disable comments
        '@eslint-community/eslint-comments/require-description': [
          'error',
          { ignore: [] },
        ],
        // Re-enable rules after disabling
        '@eslint-community/eslint-comments/disable-enable-pair': 'error',
        // No duplicate disable comments
        '@eslint-community/eslint-comments/no-duplicate-disable': 'error',
        // Unused disable comments
        '@eslint-community/eslint-comments/no-unused-disable': 'error',
      },
    },
  
    // Import sorting for all JS/TS files
    {
      name: 'import-sorting',
      files: ['src/**/*.{js,jsx,ts,tsx}'],
      plugins: {
        'simple-import-sort': simpleImportSort,
      },
      rules: {
        'simple-import-sort/imports': [
          'error',
          {
            groups: [
              // Side effect imports (e.g., import './styles.css')
              ['^\\u0000'],
              // Node.js builtins (fs, path, etc.)
              ['^node:'],
              // React and related packages
              ['^react', '^react-dom'],
              // External packages from node_modules
              ['^@?\\w'],
              // Internal aliases (@/ paths)
              ['^@/'],
              // Parent imports (../)
              ['^\\.\\.(?!/?$)', '^\\.\\./?$'],
              // Same-folder imports (./)
              ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
              // Type imports
              ['^.+\\u0000$'],
            ],
          },
        ],
        'simple-import-sort/exports': 'error',
      },
    },
  
    // Main process (Electron Node.js)
    {
      name: 'electron-main',
      files: ['src/main/**/*.ts'],
      languageOptions: {
        globals: {
          ...globals.node,
        },
      },
      rules: {
        // Allow console in main process for logging
        'no-console': 'off',
      },
    },
  
    // Preload script (Electron bridge)
    {
      name: 'electron-preload',
      files: ['src/preload/**/*.ts'],
      languageOptions: {
        globals: {
          ...globals.node,
          ...globals.browser,
        },
      },
    },
  
    // Renderer process (React + A11y + Tailwind)
    {
      name: 'renderer-react',
      files: ['src/renderer/**/*.{ts,tsx}'],
      languageOptions: {
        globals: {
          ...globals.browser,
        },
      },
      plugins: {
        react: reactPlugin,
        'react-hooks': reactHooks,
        'react-refresh': reactRefresh,
        'jsx-a11y': jsxA11y,
        tailwindcss: tailwindcss,
      },
      settings: {
        react: {
          version: 'detect',
        },
        tailwindcss: {
          // Tailwind config path (relative to cwd)
          config: 'tailwind.config.js',
          // Allow custom classnames (e.g., from CSS modules)
          callees: ['classnames', 'clsx', 'cn'],
        },
      },
      rules: {
        // React recommended rules
        ...reactPlugin.configs.recommended.rules,
        // JSX runtime (React 17+) - no need to import React
        ...reactPlugin.configs['jsx-runtime'].rules,
        // React Hooks rules
        ...reactHooks.configs.recommended.rules,
        // Accessibility rules (recommended)
        ...jsxA11y.configs.recommended.rules,
        // Tailwind CSS rules
        ...tailwindcss.configs.recommended.rules,
  
        // React Refresh for HMR
        'react-refresh/only-export-components': [
          'warn',
          { allowConstantExport: true },
        ],
        // Disable prop-types since we use TypeScript
        'react/prop-types': 'off',
  
        // A11y rule adjustments for this project
        // Allow click handlers on divs when keyboard handlers also present
        'jsx-a11y/click-events-have-key-events': 'warn',
        'jsx-a11y/no-static-element-interactions': 'warn',
        // Allow autofocus for search inputs in desktop apps
        'jsx-a11y/no-autofocus': 'off',
  
        // Tailwind CSS rule adjustments
        // Warn on class order (Prettier plugin handles sorting)
        'tailwindcss/classnames-order': 'off', // Prettier plugin handles this
        // Warn on conflicting classes
        'tailwindcss/no-contradicting-classname': 'error',
        // Warn on custom classnames that don't exist
        'tailwindcss/no-custom-classname': 'warn',
  
        // === React-Specific Rules ===
        // Consistent component definition
        'react/function-component-definition': [
          'error',
          {
            namedComponents: 'arrow-function',
            unnamedComponents: 'arrow-function',
          },
        ],
  
        // Strengthen exhaustive-deps
        'react-hooks/exhaustive-deps': 'error',
  
        // Prevent prop spreading
        'react/jsx-props-no-spreading': [
          'warn',
          {
            exceptions: ['input', 'button', 'Input', 'Button', 'textarea', 'select'],
          },
        ],
  
        // Ensure key props
        'react/jsx-key': [
          'error',
          {
            checkFragmentShorthand: true,
            checkKeyMustBeforeSpread: true,
          },
        ],
  
        // Prevent unnecessary fragments
        'react/jsx-no-useless-fragment': 'warn',
  
        // Self-closing components for consistency
        'react/self-closing-comp': [
          'error',
          {
            component: true,
            html: true,
          },
        ],
      },
    },
  
    // Test files
    {
      name: 'test-files',
      files: ['test/**/*.ts', '**/*.test.ts', '**/*.spec.ts'],
      languageOptions: {
        globals: {
          ...globals.node,
        },
        parserOptions: {
          projectService: false,
          project: './tsconfig.test.json',
        },
      },
      rules: {
        // Relax TypeScript strict rules for tests
        '@typescript-eslint/no-explicit-any': 'off',
        '@typescript-eslint/no-non-null-assertion': 'off',
        '@typescript-eslint/no-unsafe-assignment': 'off',
        '@typescript-eslint/no-unsafe-member-access': 'off',
        '@typescript-eslint/no-unsafe-call': 'off',
        '@typescript-eslint/no-unsafe-argument': 'off',
        '@typescript-eslint/no-unsafe-return': 'off',
        '@typescript-eslint/unbound-method': 'off',
  
        // Relax function/export rules for tests
        '@typescript-eslint/explicit-function-return-type': 'off',
        '@typescript-eslint/explicit-module-boundary-types': 'off',
  
        // Relax naming conventions for tests (allow describe, it, expect patterns)
        '@typescript-eslint/naming-convention': 'off',
  
        // Allow magic numbers in tests
        'sonarjs/no-hardcoded-ip': 'off',
  
        // Allow floating promises in tests (common with async test helpers)
        '@typescript-eslint/no-floating-promises': 'off',
      },
    },
  
    // Custom rule overrides for all TypeScript files
    {
      name: 'custom-rules',
      files: ['src/**/*.{ts,tsx}'],
      rules: {
        // === Core JavaScript rules ===
        'prefer-const': 'error',
        'no-var': 'error',
        eqeqeq: ['error', 'always', { null: 'ignore' }],
  
        // === TypeScript Import/Export rules ===
        '@typescript-eslint/consistent-type-imports': [
          'error',
          {
            prefer: 'type-imports',
            fixStyle: 'inline-type-imports',
          },
        ],
        '@typescript-eslint/consistent-type-exports': [
          'error',
          { fixMixedExportsWithInlineTypeSpecifier: true },
        ],
  
        // === Unused variables ===
        '@typescript-eslint/no-unused-vars': [
          'error',
          {
            argsIgnorePattern: '^_',
            varsIgnorePattern: '^_',
            caughtErrorsIgnorePattern: '^_',
          },
        ],
  
        // === Relaxed strict rules for practical use ===
        // Allow empty functions (useful for callbacks and stubs)
        '@typescript-eslint/no-empty-function': 'off',
  
        // Allow numbers/booleans in template literals (common pattern)
        '@typescript-eslint/restrict-template-expressions': [
          'error',
          {
            allowNumber: true,
            allowBoolean: true,
            allowNullish: false,
          },
        ],
  
        // Allow async functions without await (IPC handlers often need this)
        '@typescript-eslint/require-await': 'off',
  
        // Allow floating promises in event handlers (common in Electron)
        '@typescript-eslint/no-floating-promises': [
          'error',
          {
            ignoreVoid: true,
            ignoreIIFE: true,
          },
        ],
  
        // Allow promises in places that don't expect them (event handlers)
        '@typescript-eslint/no-misused-promises': [
          'error',
          {
            checksVoidReturn: {
              attributes: false,
              arguments: false,
            },
          },
        ],
  
        // Allow void expression in arrow functions shorthand
        '@typescript-eslint/no-confusing-void-expression': [
          'error',
          {
            ignoreArrowShorthand: true,
            ignoreVoidOperator: true,
          },
        ],
  
        // Prefer nullish coalescing but don't error on logical or
        '@typescript-eslint/prefer-nullish-coalescing': 'off',
  
        // Allow inferrable types (style preference)
        '@typescript-eslint/no-inferrable-types': 'off',
  
        // === Anti-Hallucination Rules ===
        // Explicit return types
        '@typescript-eslint/explicit-function-return-type': [
          'warn',
          {
            allowExpressions: true,
            allowTypedFunctionExpressions: true,
            allowHigherOrderFunctions: true,
            allowDirectConstAssertionInArrowFunctions: true,
          },
        ],
  
        // Explicit types for exported functions (minimum requirement)
        '@typescript-eslint/explicit-module-boundary-types': 'warn',
  
        // Prevent variable shadowing
        '@typescript-eslint/no-shadow': 'error',
  
        // === Naming Conventions ===
        '@typescript-eslint/naming-convention': [
          'warn',
          // Imports can be camelCase or PascalCase (React, ReactDOM, App, etc.)
          {
            selector: 'import',
            format: ['camelCase', 'PascalCase'],
          },
          // Default: variables and parameters in camelCase
          {
            selector: 'default',
            format: ['camelCase'],
            leadingUnderscore: 'allow',
          },
          // Static readonly class properties can be UPPER_CASE
          {
            selector: 'classProperty',
            modifiers: ['static', 'readonly'],
            format: ['camelCase', 'UPPER_CASE'],
          },
          // Variables: camelCase or UPPER_CASE for constants
          {
            selector: 'variable',
            format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
            leadingUnderscore: 'allow',
          },
          // Functions: camelCase (includes type guards like isXxx, builders like buildXxx)
          {
            selector: 'function',
            format: ['camelCase', 'PascalCase'],
          },
          // Parameters: camelCase, allow leading underscore for unused
          {
            selector: 'parameter',
            format: ['camelCase'],
            leadingUnderscore: 'allow',
          },
          // Types and interfaces in PascalCase
          {
            selector: 'typeLike',
            format: ['PascalCase'],
          },
          // Interfaces should NOT start with I (modern convention)
          {
            selector: 'interface',
            format: ['PascalCase'],
            custom: { regex: '^I[A-Z]', match: false },
          },
          // Enum members in PascalCase or UPPER_CASE
          {
            selector: 'enumMember',
            format: ['PascalCase', 'UPPER_CASE'],
          },
          // Object literal properties: allow any format (for API compatibility)
          {
            selector: 'objectLiteralProperty',
            format: null,
          },
          // Type properties: allow any format (for type definitions matching APIs)
          {
            selector: 'typeProperty',
            format: null,
          },
        ],
  
        // === Import Restrictions ===
        // Note: boundaries/element-types handles main/renderer separation
        'no-restricted-imports': [
          'error',
          {
            patterns: [
              // Prevent deep relative imports - use @/ aliases
              {
                group: ['../**/..'],
                message: 'Avoid deep relative imports, use @/ aliases',
              },
            ],
          },
        ],
  
        // === Mutation Prevention ===
        'no-param-reassign': [
          'error',
          {
            props: true,
            ignorePropertyModificationsFor: ['draft', 'acc', 'ctx', 'state', 'req', 'res'],
          },
        ],
  
        // === SonarJS rule adjustments ===
        // Cognitive complexity - warn instead of error for gradual adoption
        'sonarjs/cognitive-complexity': 'off',
        // Allow some duplication in similar but not identical code
        'sonarjs/no-duplicate-string': 'off',
        // Relax for Electron IPC patterns (many similar switch cases)
        'sonarjs/no-small-switch': 'off',
        // Allow nested ternaries in JSX (common React pattern)
        'sonarjs/no-nested-conditional': 'off',
  
        // === Security rule adjustments (Code Protection) ===
        // These catch common security mistakes
        'security/detect-eval-with-expression': 'error',
        // Disabled: This is a desktop file reader app - file system access is expected
        'security/detect-non-literal-fs-filename': 'off',
        // Disabled: Dynamic patterns are intentional in this app
        'security/detect-non-literal-regexp': 'off',
        // Disabled: Often false positives with typed code
        'security/detect-object-injection': 'off',
        'security/detect-child-process': 'warn',
        'security/detect-non-literal-require': 'warn',
        'security/detect-possible-timing-attacks': 'warn',
      },
    },
  
    // === IMPORTANT: eslint-config-prettier MUST be LAST ===
    // This disables all ESLint rules that conflict with Prettier
    // Prettier handles formatting, ESLint handles code quality
    eslintConfigPrettier,
  ]);
  