clj-kondo
A guide to using clj-kondo for Clojure code linting, including configuration, built-in linters, and writing custom hooks.
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
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.