Claude
Skills
Sign in
Back

clj-kondo

Included with Lifetime
$97 forever

A guide to using clj-kondo for Clojure code linting, including configuration, built-in linters, and writing custom hooks.

Writing & Docs

What this skill does


# clj-kondo Skill Guide

A comprehensive guide to using clj-kondo for Clojure code linting, including configuration, built-in linters, and writing custom hooks.

## Table of Contents

1. [Introduction](#introduction)
2. [Installation](#installation)
3. [Getting Started](#getting-started)
4. [Configuration](#configuration)
5. [Built-in Linters](#built-in-linters)
6. [Custom Hooks](#custom-hooks)
7. [IDE Integration](#ide-integration)
8. [CI/CD Integration](#cicd-integration)
9. [Best Practices](#best-practices)
10. [Troubleshooting](#troubleshooting)

## Introduction

### What is clj-kondo?

clj-kondo is a fast, static analyzer and linter for Clojure code. It:

- Catches syntax errors and common mistakes
- Enforces code style and best practices
- Provides immediate feedback during development
- Supports custom linting rules via hooks
- Integrates with all major editors and CI systems
- Requires no project dependencies or runtime

### Why Use clj-kondo?

- **Fast**: Native binary with instant startup
- **Accurate**: Deep understanding of Clojure semantics
- **Extensible**: Custom hooks for domain-specific rules
- **Zero configuration**: Works out of the box
- **Cross-platform**: Native binaries for Linux, macOS, Windows
- **IDE integration**: Works with Emacs, VS Code, IntelliJ, Vim, etc.

## Installation

### macOS/Linux (Homebrew)

```bash
brew install clj-kondo/brew/clj-kondo
```

### Manual Binary Installation

Download from [GitHub Releases](https://github.com/clj-kondo/clj-kondo/releases):

```bash
# Linux
curl -sLO https://raw.githubusercontent.com/clj-kondo/clj-kondo/master/script/install-clj-kondo
chmod +x install-clj-kondo
./install-clj-kondo

# Place in PATH
sudo mv clj-kondo /usr/local/bin/
```

### Via Clojure CLI

```bash
clojure -Ttools install-latest :lib io.github.clj-kondo/clj-kondo :as clj-kondo
clojure -Tclj-kondo run :lint '"src"'
```

### Verify Installation

```bash
clj-kondo --version
# clj-kondo v2024.11.14
```

## Getting Started

### Basic Usage

Lint a single file:

```bash
clj-kondo --lint src/myapp/core.clj
```

Lint a directory:

```bash
clj-kondo --lint src
```

Lint multiple paths:

```bash
clj-kondo --lint src test
```

### Understanding Output

```
src/myapp/core.clj:12:3: warning: unused binding x
src/myapp/core.clj:25:1: error: duplicate key :name
linting took 23ms, errors: 1, warnings: 1
```

Format: `file:line:column: level: message`

### Output Formats

**Human-readable (default):**
```bash
clj-kondo --lint src
```

**JSON (for tooling):**
```bash
clj-kondo --lint src --config '{:output {:format :json}}'
```

**EDN:**
```bash
clj-kondo --lint src --config '{:output {:format :edn}}'
```

### Creating Cache

For better performance on subsequent runs:

```bash
clj-kondo --lint "$(clojure -Spath)" --dependencies --parallel --copy-configs
```

This caches analysis of dependencies and copies their configurations.

## Configuration

### Configuration File Location

clj-kondo looks for `.clj-kondo/config.edn` in:
1. Current directory
2. Parent directories (walking up)
3. Home directory (`~/.config/clj-kondo/config.edn`)

### Basic Configuration

`.clj-kondo/config.edn`:

```clojure
{:linters {:unused-binding {:level :warning}
           :unused-namespace {:level :warning}
           :unresolved-symbol {:level :error}
           :invalid-arity {:level :error}}
 :output {:pattern "{{LEVEL}} {{filename}}:{{row}}:{{col}} {{message}}"}}
```

### Linter Levels

- `:off` - Disable the linter
- `:info` - Informational message
- `:warning` - Warning (default for most)
- `:error` - Error (fails build)

### Global Configuration

Disable specific linters:

```clojure
{:linters {:unused-binding {:level :off}}}
```

Configure linter options:

```clojure
{:linters {:consistent-alias {:aliases {clojure.string str
                                        clojure.set set}}}}
```

### Local Configuration

Suppress warnings in specific namespaces:

```clojure
{:linters {:unused-binding {:level :off
                            :exclude-ns [myapp.test-helpers]}}}
```

### Inline Configuration

In source files:

```clojure
;; Disable for entire namespace
(ns myapp.core
  {:clj-kondo/config '{:linters {:unused-binding {:level :off}}}})

;; Disable for specific form
#_{:clj-kondo/ignore [:unused-binding]}
(let [x 1] 2)

;; Disable all linters for form
#_{:clj-kondo/ignore true}
(some-legacy-code)
```

### Configuration Merging

Configurations merge in this order:
1. Built-in defaults
2. Home directory config
3. Project config (`.clj-kondo/config.edn`)
4. Inline metadata

## Built-in Linters

### Namespace and Require Linters

**`:unused-namespace`** - Warns about unused required namespaces

```clojure
(ns myapp.core
  (:require [clojure.string :as str])) ;; Warning if 'str' never used

;; Fix: Remove unused require
```

**`:unsorted-required-namespaces`** - Enforces sorted requires

```clojure
{:linters {:unsorted-required-namespaces {:level :warning}}}
```

**`:namespace-name-mismatch`** - Ensures namespace matches file path

```clojure
;; In src/myapp/utils.clj
(ns myapp.helpers) ;; Error: should be myapp.utils
```

### Binding and Symbol Linters

**`:unused-binding`** - Warns about unused let bindings

```clojure
(let [x 1
      y 2] ;; Warning: y is unused
  x)

;; Fix: Remove or prefix with underscore
(let [x 1
      _y 2]
  x)
```

**`:unresolved-symbol`** - Catches typos and undefined symbols

```clojure
(defn foo []
  (bar)) ;; Error: unresolved symbol bar

;; Fix: Define bar or require it
```

**`:unused-private-var`** - Warns about unused private definitions

```clojure
(defn- helper []) ;; Warning if never called

;; Fix: Remove or use it
```

### Function and Arity Linters

**`:invalid-arity`** - Catches incorrect function call arities

```clojure
(defn add [a b] (+ a b))
(add 1) ;; Error: wrong arity, expected 2 args

;; Fix: Provide correct number of arguments
```

**`:missing-body-in-when`** - Warns about empty when blocks

```clojure
(when condition) ;; Warning: missing body

;; Fix: Add body or use when-not/if
```

### Collection and Syntax Linters

**`:duplicate-map-key`** - Catches duplicate keys in maps

```clojure
{:name "Alice"
 :age 30
 :name "Bob"} ;; Error: duplicate key :name
```

**`:duplicate-set-key`** - Catches duplicate values in sets

```clojure
#{1 2 1} ;; Error: duplicate set element
```

**`:misplaced-docstring`** - Warns about incorrectly placed docstrings

```clojure
(defn foo
  [x]
  "This is wrong" ;; Warning: docstring after params
  x)

;; Fix: Place before params
(defn foo
  "This is correct"
  [x]
  x)
```

### Type and Spec Linters

**`:type-mismatch`** - Basic type checking

```clojure
(inc "string") ;; Warning: expected number
```

**`:invalid-arities`** - Checks arities for core functions

```clojure
(map) ;; Error: map requires at least 2 arguments
```

## Custom Hooks

### What Are Hooks?

Hooks are custom linting rules written in Clojure that analyze your code using clj-kondo's analysis data. They enable:

- Domain-specific linting rules
- API usage validation
- Deprecation warnings
- Team convention enforcement
- Advanced static analysis

### When to Use Hooks

Use hooks when:
- Built-in linters don't cover your needs
- You have library-specific patterns to enforce
- You want to warn about deprecated APIs
- You need to validate domain-specific logic
- You want to enforce team coding standards

### Hook Architecture

Hooks receive:
1. **Analysis context** - Information about the code being analyzed
2. **Node** - The AST node being examined

Hooks return:
1. **Findings** - Lint warnings/errors to report
2. **Updated analysis** - Modified context for downstream analysis

### Creating Your First Hook

#### 1. Hook Directory Structure

```
.clj-kondo/
  config.edn
  hooks/
    my_hooks.clj
```

#### 2. Basic Hook Template

`.clj-kondo/hooks/my_hooks.clj`:

```clojure
(ns hooks.my-hooks
  (:require [clj-kondo.hooks-api :as api]))

(defn my-hook
  "Description of what this hook does"
  [{:keys [node]

Related in Writing & Docs