linthis
A fast, cross-platform multi-language linter and formatter written in Rust.
Features
- 🚀 Single Command: Run linting, formatting, security, and complexity checks simultaneously
- 🌍 Multi-Language Support: Rust, Python, TypeScript, JavaScript, Go, Java, C++, Swift, Kotlin, Lua, and more
- 🎯 Auto-Detection: Automatically detect programming languages used in your project
- 🔒 Security Scanning (SAST): Built-in secrets detection + OpenGrep/Semgrep, Bandit, Gosec, Flawfinder integration
- 📊 Complexity Analysis: Cyclomatic/cognitive complexity per function with threshold enforcement
- ⚙️ Flexible Configuration: Support for project config, global config, and CLI parameters
- 📦 Plugin System: Share and reuse configurations via Git repositories
- 🎨 Format Presets: Support for popular code styles like Google, Airbnb, Standard
- ⚡ Parallel Processing: Leverage multi-core CPU for faster file processing with per-file caching
- 🤖 AI Code Review:
linthis reviewanalyzes diffs with AI and creates PR/MR automatically - 💾 Format with Backup:
linthis formatcreates backups before formatting, supports--undo - 🔄 Auto Re-stage: When running in staged mode (
-s), formatted files are automatically re-staged - 🔌 Plugin Hook Bundling: Plugins can ship custom git and agent hook scripts — auto-installed when the plugin is added
Installation
Method 1: Install via PyPI (Recommended for Python users)
# Using pip
# Using uv (recommended)
# pip install uv
Method 2: Install via Cargo (Recommended for Rust users)
Method 3: Build from Source
Quick Start
Install linthis, add a plugin, set up hooks, and run your first check — all in under a minute.
# 1. Install
# 2. Add team plugin (-g is user scope, use your team's plugin URL)
# 3. Install hooks (-g is user scope)
# 4. Run lint check
# 5. Check staged files before commit
See more video tutorials in the Video Tutorials page.
Initialize Configuration (Optional)
# Create project configuration file
# Create global configuration file
# Project-level hooks
# Global hooks (apply to all repos on this machine)
# Force overwrite existing files
Basic Usage
# Check and format current directory (default behavior)
# Check and format specific directories
# Check only, no formatting
# Format only, no checking
# Check Git staged files (suitable for pre-commit hook)
# Check all locally modified files (staged + unstaged)
# Run specific checks (default: lint + security + complexity)
Specify Languages
# Check specific language
# Check multiple languages
Exclude Files
# Exclude specific patterns
Plugin System
linthis supports Git-based configuration plugins for easy sharing of code standards across projects and teams.
Add Plugin
# Add plugin to project config (.linthis/config.toml)
# Example: Add a custom plugin
# Add to global config (~/.linthis/config.toml)
Use Plugin
Plugins are automatically loaded when running linthis. After adding a plugin:
# Plugin configs are auto-loaded
# Combine with other options
# Check only
# Format only
# Check and format files staged
Remove Plugin
# Remove plugin from project config
# Remove plugin from global config
# Supports flexible parameter ordering
View and Manage Plugins
# View project config plugins
# View global config plugins
# Sync (update) plugins
# Initialize new plugin
# Validate plugin structure
# Clean plugin cache
Configuration Files
Project Configuration
Use linthis init to create the configuration file:
This creates .linthis/config.toml in your project root:
# Specify languages to check (omit for auto-detection)
= ["rust", "python", "javascript"]
# Exclude files and directories
= [
"target/**",
"node_modules/**",
"*.generated.rs",
"dist/**"
]
# Maximum cyclomatic complexity
= 20
# Format preset
= "google" # Options: google, airbnb, standard
# Configure plugins
[]
= [
{ = "official" },
{ = "myplugin", = "https://github.com/zhlinh/linthis-plugin.git", = "main" }
]
# Checks to run (default: lint + security + complexity)
[]
= ["lint", "security", "complexity"]
# Security check settings
[]
= "sast" # sca, sast, all
= "high" # critical, high, medium, low
# Complexity check settings
[]
= 15
= true
# Language-specific configuration
# [rust]
# max_complexity = 15
# [python]
# excludes = ["*_test.py"]
Global Configuration
Global configuration file is located at ~/.linthis/config.toml, with the same format as project config.
Configuration Priority
Configuration merge priority (from high to low):
- CLI Parameters:
--option value - Project Config:
.linthis/config.toml - Global Config:
~/.linthis/config.toml - Built-in Defaults
For tool-specific configs (ruff.toml, .eslintrc.js, etc.), the priority is:
- Local manual configs (highest) - ruff.toml, pyproject.toml, .eslintrc.js in project
- CLI plugin configs - from
--use-pluginoption - Project plugin configs - from
.linthis/config.tomlplugins section - Global plugin configs - from
~/.linthis/config.tomlplugins - Tool defaults (lowest)
Configuration Management
linthis provides a config subcommand for convenient command-line configuration management without manual TOML editing.
Array Field Operations
Supported array fields: includes, excludes, languages
Add Values (add)
# Add to project config
# Add to global config (-g or --global)
# Add multiple values (automatically deduped)
Remove Values (remove)
# Remove from project config
# Remove from global config
Clear Field (clear)
# Clear project config field
# Clear global config field
Scalar Field Operations
Supported scalar fields: max_complexity, preset, verbose
Set Value (set)
# Set complexity limit
# Set format preset (google, standard, airbnb)
# Set verbose output
Unset Value (unset)
# Remove field from project config
# Remove field from global config
Query Operations
Get Single Field Value (get)
# View project config field
# View global config field
List All Configuration (list)
# List project config
# List global config
# Verbose mode (show all fields including empty values)
Configuration Management Examples
# Initialize project config
# View config
# Adjust config
# Set global defaults
Configuration Migration
linthis can automatically detect and migrate existing linter/formatter configurations to linthis format.
Supported Tools
| Tool | Detected Files |
|---|---|
| ESLint | .eslintrc.js, .eslintrc.json, .eslintrc.yml, .eslintrc, eslint.config.js, package.json[eslintConfig] |
| Prettier | .prettierrc, .prettierrc.json, .prettierrc.yml, .prettierrc.js, prettier.config.js, package.json[prettier] |
| Black | pyproject.toml[tool.black] |
| isort | pyproject.toml[tool.isort] |
Migration Commands
# Auto-detect and migrate all configs
# Migrate specific tool only
# Preview changes without applying
# Create backup of original files
# Verbose output
Migration Output
Migrated configurations are placed in .linthis/configs/{language}/:
- ESLint →
.linthis/configs/javascript/.eslintrc.js - Prettier →
.linthis/configs/javascript/prettierrc.js - Black/isort →
.linthis/configs/python/ruff.toml
Initialize Configuration File
Use the init subcommand to explicitly create configuration files:
# Create project config (.linthis/config.toml)
# Create global config (~/.linthis/config.toml)
# Backward compatible: can also use --init flag
Auto-Create Configuration Files
When using the config command, configuration files are automatically created if they don't exist:
- Project Config: Creates
.linthis/config.tomlin current directory - Global Config: Creates
config.tomlin~/.linthis/directory
All modifications preserve TOML file format and comments.
Command Line Options
Main Command Options
| Short | Long | Description | Example |
|---|---|---|---|
-i |
--include |
Specify files or directories to check | -i src -i lib |
-e |
--exclude |
Exclude patterns (can be used multiple times) | -e "*.test.js" |
-c |
--check-only |
Check only, no formatting | -c |
-f |
--format-only |
Format only, no checking | -f |
-s |
--staged |
Check only Git staged files | -s |
-m |
--modified |
Check all locally modified files (staged + unstaged) | -m |
-l |
--lang |
Specify languages (comma-separated) | -l python,rust |
-o |
--output |
Output format: human, json, github-actions | -o json |
-v |
--verbose |
Verbose output | -v |
-q |
--quiet |
Quiet mode (errors only) | -q |
--config |
Specify config file path | --config custom.toml |
|
--init |
Initialize .linthis/config.toml config file | --init |
|
--preset |
Format preset | --preset google |
|
--no-default-excludes |
Disable default exclude rules | --no-default-excludes |
|
--no-gitignore |
Disable .gitignore rules | --no-gitignore |
|
--no-plugin |
Skip loading plugins, use default config | --no-plugin |
Plugin Management Subcommands
| Command | Short | Long | Description |
|---|---|---|---|
plugin add <alias> <url> |
-g |
--global |
Add to global config |
--ref |
Specify Git reference | ||
plugin remove <alias> |
-g |
--global |
Remove from global config |
plugin list |
-g |
--global |
Show global config plugins |
-v |
--verbose |
Show detailed info | |
plugin clean |
--all |
Clean all caches | |
plugin init <name> |
Initialize new plugin | ||
plugin validate <path> |
Validate plugin structure |
Configuration Management Subcommands
| Command | Short | Long | Description |
|---|---|---|---|
config add <field> <value> |
-g |
--global |
Add value to array field |
config remove <field> <value> |
-g |
--global |
Remove value from array field |
config clear <field> |
-g |
--global |
Clear array field |
config set <field> <value> |
-g |
--global |
Set scalar field value |
config unset <field> |
-g |
--global |
Remove scalar field |
config get <field> |
-g |
--global |
Get field value |
config list |
-g |
--global |
List all configuration |
-v |
--verbose |
Show detailed info (including empty values) | |
config migrate |
--from |
Migrate from specific tool | |
--dry-run |
Preview changes without applying | ||
--backup |
Create backup of original files | ||
-v |
--verbose |
Show detailed output |
Supported array fields: includes, excludes, languages
Supported scalar fields: max_complexity, preset, verbose
Init Subcommand
| Command | Short | Long | Description |
|---|---|---|---|
init |
-g |
--global |
Create global config file |
--with-hook |
Also install git hook after init | ||
--force |
Force overwrite existing files |
Created configuration files:
- Without
-g: Creates.linthis/config.toml(current directory) - With
-g: Creates~/.linthis/config.toml(global config)
Hook Subcommand
| Command | Short | Long | Description |
|---|---|---|---|
hook install |
--type |
Hook type (git/git-with-agent/agent/prek/prek-with-agent) | |
--event |
Hook event (pre-commit/pre-push/commit-msg) | ||
-g |
--global |
Install globally: agent type → user home dir; others → ~/.config/git/hooks/ + core.hooksPath |
|
--provider |
AI provider: claude/codex/gemini/cursor/droid/auggie/codebuddy. Supports provider/model shorthand (e.g. claude/opus). For --type agent: installs rules files. For *-with-agent: uses headless CLI to auto-fix. |
||
--provider-args |
Extra arguments passed to the AI agent CLI (e.g. "--model opus") |
||
-c |
--check-only |
Hook only runs check | |
-f |
--format-only |
Hook only runs format | |
--force |
Force overwrite existing hook | ||
-y |
--yes |
Non-interactive mode | |
hook uninstall |
--event |
Hook event to uninstall | |
-g |
--global |
Uninstall global hook | |
--all |
Uninstall all hooks | ||
-y |
--yes |
Non-interactive mode | |
hook status |
Show git hook status (Project Hooks and Global Hooks sections) | ||
hook check |
Check for hook conflicts | ||
hook sync |
Re-sync all installed hooks and agent skills | ||
-g |
--global |
Sync global hooks |
Hook types:
git: Traditional git hook (default)git-with-agent: git hook + AI agent auto-fix on failureagent: AI agent hook (Claude, Cursor, Windsurf, etc.)prek: Rust-based pre-commit tool (faster)prek-with-agent: prek hook + AI agent auto-fix on failure
Global hooks: Use -g / --global with any hook type. For agent type, installs rules to the user home directory. For all other types, installs to ~/.config/git/hooks/ and sets git config --global core.hooksPath. Local hooks take priority over global hooks (Strategy B).
Hook events:
pre-commit: Run before commit (default, checks staged files)pre-push: Run before push (checks all files)commit-msg: Validate commit message format (callslinthis cmsg "$1")
Each hook event generates a separate per-event skill file for agent integrations (e.g., lt-lint for pre-commit, lt-cmsg for commit-msg, lt-review for pre-push). All skills belong to a single unified plugin bundle lt.
cmsg Subcommand
Validate commit message format directly — without going through a hook.
| Command | Description |
|---|---|
cmsg <msg-or-file> |
Validate a commit message string or file path |
cmsg <file> --auto-fix |
AI rewrite on failure (writes result back to file) |
# Validate a message string directly
# Validate from a file (git hook usage)
# AI rewrite on failure (writes result back to file)
# Install the commit-msg hook (calls `linthis cmsg "$1"` automatically)
The default format follows Conventional Commits:
type(scope)?: description — where type is one of feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.
The pattern is configurable via .linthis/config.toml:
[]
= "^(feat|fix|docs|...)\\(\\S+\\)?: .{1,72}"
format Subcommand
Format files with automatic backup and undo support.
A backup is created before each format operation. Use --undo to revert.
Note: When running
linthis -s(staged mode), formatted files are automatically re-staged — no manualgit addneeded.
review Subcommand
AI-powered code review with PR/MR creation.
Supported platforms: GitHub (gh), GitLab (glab).
Configure in .linthis/config.toml:
[]
= true
= "claude-cli"
[]
= ["alice", "bob"]
Supported Languages
| Language | Linter | Formatter |
|---|---|---|
| Rust | clippy | rustfmt |
| Python | ruff, pylint, flake8 | ruff, black |
| TypeScript | eslint | prettier |
| JavaScript | eslint | prettier |
| Go | golangci-lint | gofmt |
| Java | checkstyle | google-java-format |
| C | clang-tidy, cppcheck | clang-format |
| C++ | clang-tidy, cpplint, cppcheck | clang-format |
| Objective-C | clang-tidy | clang-format |
| Swift | swiftlint | swift-format |
| Kotlin | ktlint, detekt | ktlint |
| Lua | luacheck | stylua |
| Dart | dart analyze | dart format |
| Shell/Bash | shellcheck | shfmt |
| Ruby | rubocop | rubocop |
| PHP | phpcs | php-cs-fixer |
| Scala | scalafix | scalafmt |
| C# | dotnet format | dotnet format |
Editor Plugins
linthis provides official plugins for popular editors, offering seamless integration with format-on-save, manual lint/format commands, and configurable settings.
VSCode
Install from VS Marketplace or search "linthis" in VSCode Extensions.
Features:
- Format on Save (configurable)
- Manual Lint/Format commands via Command Palette
- Configurable executable path and additional arguments
- Status bar integration
Installation via Command Palette:
ext install zhlinh.linthis
Configuration (settings.json):
📁 Source: vscode-linthis
JetBrains (IntelliJ IDEA, WebStorm, PyCharm, etc.)
Install from JetBrains Marketplace or search "linthis" in IDE Settings → Plugins.
Features:
- Format on Save (configurable)
- Manual Lint/Format via Tools menu
- Configurable executable path and additional arguments
- Settings UI in Preferences → Tools → Linthis
Installation:
- Open Settings/Preferences → Plugins
- Search for "linthis"
- Click Install and restart IDE
Configuration:
- Settings → Tools → Linthis
- Enable/disable Format on Save
- Set custom executable path
- Add additional command-line arguments
📁 Source: jetbrains-linthis
Neovim
Install using your favorite plugin manager. Distributed via GitHub.
lazy.nvim (Recommended)
-- For monorepo (plugin in subdirectory)
-- For standalone repository
packer.nvim
-- For monorepo
use
vim-plug
" For monorepo
Plug 'zhlinh/linthis', { 'rtp': 'nvim-linthis' }
Features:
- Format on Save (configurable)
- Commands:
:LinthisLint,:LinthisFormat,:LinthisLintFormat - Configurable via
setup()options
Configuration:
require.
📁 Source: nvim-linthis
Usage Scenarios
Pre-commit Hook
Method 1: Using prek (Recommended for Teams)
prek is a high-performance Git hooks manager written in Rust, fully compatible with pre-commit config format but much faster.
Install prek:
# Using cargo
# Or using pip
Create .pre-commit-config.yaml in your project:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: linthis
name: linthis
entry: linthis --staged --check-only
language: system
pass_filenames: false
Install hook:
Method 2: Traditional Git Hook (Project-level)
Add to .git/hooks/pre-commit:
#!/bin/sh
Or use linthis to create it automatically:
Method 3: Using pre-commit Framework
Using the pre-commit framework:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: linthis
name: linthis
entry: linthis --staged --check-only
language: system
pass_filenames: false
CI/CD Integration
GitHub Actions
name: Lint
on:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install linthis
run: pip install linthis
- name: Run linthis
run: linthis --check-only --output github-actions
GitLab CI
lint:
image: rust:latest
script:
- cargo install linthis
- linthis --check-only
Creating Custom Plugins
1. Initialize Plugin
2. Edit Plugin Configuration
Edit linthis-plugin.toml:
[]
= "my-company-standards"
= "1.0.0"
= "My company's coding standards"
[]
= 2
[]
= "P0"
= [".flake8"]
[]
= "P1"
= ["pyproject.toml"]
3. Add Configuration Files
# Add your config files to corresponding language directories
3b. (Optional) Bundle Hook Overrides
Create linthis-hook.toml in the plugin root to ship custom git/agent hooks. Use plugin = "self" — it is replaced with the user's alias when they add the plugin.
# linthis-hook.toml — bundled hook overrides
[]
= { = { = "self", = "hooks/git/pre-commit" } }
[]
= { = { = "self", = "hooks/agent/plugins/lt" } }
[]
= { = { = "self", = "hooks/agent/hook/stop/claude/settings.json" } }
Place the referenced files in the plugin repo. When users run linthis plugin add company <url>, these entries are automatically merged into their .linthis/config.toml.
4. Publish to Git
5. Use Your Plugin
FAQ
Q: How to specify multiple paths?
Q: How to check only specific file types?
Q: Where is the plugin cache?
- macOS:
~/Library/Caches/linthis/plugins - Linux:
~/.cache/linthis/plugins - Windows:
%LOCALAPPDATA%\linthis\cache\plugins
Q: How to update plugins?
Q: What is the plugin Git reference (ref) used for?
The ref can specify:
- Branch name:
--ref main - Tag:
--ref v1.0.0 - Commit hash:
--ref abc1234
This allows you to lock plugin versions or use development versions.
Documentation
- Plugin Auto-Sync - Automatic plugin synchronization (inspired by oh-my-zsh)
- Self Auto-Update - Automatic self-update functionality
Development
Build
Test
Release
Contributing
Issues and Pull Requests are welcome!
License
MIT License - See LICENSE file for details