# Creating Plugins
This guide explains how to create and distribute linthis plugins.
## What is a Plugin?
A linthis plugin is a Git repository containing:
- A `linthis-plugin.toml` manifest file
- One or more language configuration files (TOML, YAML, or JSON)
- An optional `linthis-hook.toml` that bundles hook source overrides
- Optional custom rules and presets
Plugins allow you to share lint configurations **and git/agent hook setups** across projects or teams.
## Plugin Structure
```
my-linthis-plugin/
├── linthis-plugin.toml # Required: Plugin manifest
├── linthis-hook.toml # Optional: Hook source overrides (auto-merged on plugin add)
├── config.toml # Main lint configuration
├── hooks/ # Optional: Custom hook files
│ ├── git/
│ │ └── pre-commit # Custom pre-commit script
│ └── agent/
│ └── plugins/
│ ├── _default/ # Default fallback (all providers)
│ │ └── lt/ # Plugin bundle
│ │ ├── skills/lt-lint/SKILL.md
│ │ ├── commands/
│ │ ├── memories/TOPLEVEL.md
│ │ └── hooks/hooks.json
│ └── claude/ # Provider-specific override (optional)
│ └── lt/
│ ├── skills/lt-lint/SKILL.md
│ └── hooks/hooks.json # Provider hooks (e.g. stop hook)
├── rules/ # Optional: Additional rule configs
│ ├── strict.toml
│ └── relaxed.toml
└── README.md # Optional: Documentation
```
## Hook Bundling via `linthis-hook.toml`
A plugin can ship a `linthis-hook.toml` alongside its lint configs. This file declares `[hook.*]` source overrides that point back into the plugin itself using `plugin = "self"`.
When a user runs `linthis plugin add <alias> <url>`, linthis automatically:
1. Replaces every `plugin = "self"` reference with `plugin = "<alias>"` (the user's chosen alias)
2. Non-overwritingly merges `[hook.*]` entries into the user's `.linthis/config.toml`
After this, running `linthis hook install` will pick up the plugin's custom hook scripts and agent bundles automatically — no manual configuration needed.
### Example `linthis-hook.toml`
```toml
# Bundled inside the plugin. References use plugin = "self".
[hook.git]
pre-commit = { source = { plugin = "self", file = "hooks/git/pre-commit" } }
[hook.agent.plugins._default]
"lt" = { source = { plugin = "self", file = "hooks/agent/plugins/_default/lt" } }
[hook.agent.plugins.claude]
"lt" = { source = { plugin = "self", file = "hooks/agent/plugins/claude/lt" } }
```
After `linthis plugin add myteam <url>`, `plugin = "self"` becomes `plugin = "myteam"` and all entries are merged into the user's `.linthis/config.toml`.
## Plugin Manifest
The `linthis-plugin.toml` manifest describes your plugin:
```toml
[plugin]
# Required fields
name = "my-plugin"
version = "1.0.0"
# Optional fields
description = "Custom lint rules for my organization"
author = "Your Name <email@example.com>"
repository = "https://github.com/username/my-linthis-plugin"
license = "MIT"
# Minimum linthis version required (optional)
min_linthis_version = "0.1.0"
[config]
# Path to the main configuration file
path = "config.toml"
# Optional: Additional config files that can be referenced
# [config.presets]
# strict = "rules/strict.toml"
# relaxed = "rules/relaxed.toml"
```
## Configuration File
The main configuration file follows the standard linthis config format:
```toml
# config.toml
# Exclude patterns (will be merged with project config)
excludes = ["vendor/**", "third_party/**"]
# Default max complexity for this plugin's style guide
max_complexity = 15
# Rule modifications
[rules]
disable = ["E501"] # Disable line length checks
[rules.severity]
"W0612" = "error" # Treat unused variables as errors
# Custom rules
[[rules.custom]]
code = "org/no-fixme"
severity = "error"
suggestion = "Create a tracking issue or resolve the FIXME"
[[rules.custom]]
code = "org/copyright-header"
pattern = "^(?!// Copyright)"
message = "Missing copyright header"
severity = "warning"
extensions = ["rs", "go", "java"]
# Language-specific settings
[rust]
max_complexity = 12
[python]
excludes = ["*_test.py", "test_*.py"]
```
## Creating Your First Plugin
### Step 1: Create Repository
```bash
mkdir my-linthis-plugin
cd my-linthis-plugin
git init
```
### Step 2: Create Manifest
Create `linthis-plugin.toml`:
```toml
[plugin]
name = "my-plugin"
version = "1.0.0"
description = "My custom lint configuration"
[config]
path = "config.toml"
```
### Step 3: Create Configuration
Create `config.toml`:
```toml
# My organization's lint rules
excludes = ["generated/**"]
max_complexity = 20
[[rules.custom]]
code = "custom/no-console-log"
pattern = "console\\.log"
message = "Remove console.log before committing"
severity = "warning"
languages = ["typescript", "javascript"]
```
### Step 4: Test Locally
Before publishing, test your plugin locally:
```bash
# In your project's .linthis/config.toml
[plugins]
sources = [
{ name = "local-test", url = "file:///path/to/my-linthis-plugin" }
]
```
Then run:
```bash
linthis plugin init
linthis -c
```
### Step 5: Publish
Push to GitHub or any Git host:
```bash
git add .
git commit -m "Initial plugin release"
git tag v1.0.0
git push origin main --tags
```
## Using Your Plugin
Add to your project's `.linthis/config.toml`:
```toml
[plugins]
sources = [
{ name = "my-plugin", url = "https://github.com/username/my-linthis-plugin.git" }
]
```
Or pin to a specific version:
```toml
[plugins]
sources = [
{ name = "my-plugin", url = "https://github.com/username/my-linthis-plugin.git", ref = "v1.0.0" }
]
```
## Plugin Commands
```bash
# Initialize/download plugins
linthis plugin init
# List installed plugins
linthis plugin list
# Update all plugins to latest
linthis plugin update
# Update specific plugin
linthis plugin update my-plugin
# Clean plugin cache
linthis plugin clean
```
## Configuration Merging
Plugin configurations are merged with your project configuration. The precedence is:
1. **CLI arguments** (highest)
2. **Project config** (`.linthis/config.toml`)
3. **Plugin configs** (in order listed)
4. **User config** (`~/.linthis/config.toml`)
5. **Built-in defaults** (lowest)
Array fields (`excludes`, `includes`, `rules.disable`, `rules.custom`) are **extended** (added to), while scalar fields are **overridden**.
## Best Practices
### 1. Version Your Plugin
Use semantic versioning and Git tags:
```bash
git tag v1.0.0 # Initial release
git tag v1.1.0 # New features (backward compatible)
git tag v2.0.0 # Breaking changes
```
### 2. Document Your Rules
Add comments explaining each custom rule:
```toml
# Prevent debug code from being committed
[[rules.custom]]
code = "org/no-debug"
```
### 3. Set Minimum Version
If your plugin uses features from a specific linthis version:
```toml
[plugin]
min_linthis_version = "0.2.0"
```
### 4. Provide Presets
For flexibility, offer multiple configuration levels:
```
my-plugin/
├── config.toml # Default (recommended) settings
├── presets/
│ ├── strict.toml # Stricter rules for CI
│ └── relaxed.toml # More lenient for prototyping
```
### 5. Test Thoroughly
Before releasing, test your plugin:
- On multiple languages your rules target
- With existing project configurations
- After linthis updates
## Example Plugins
### Organization Style Guide
```toml
# linthis-plugin.toml
[plugin]
name = "acme-style"
version = "2.0.0"
description = "ACME Corp coding standards"
[config]
path = "config.toml"
```
```toml
# config.toml
max_complexity = 15
preset = "google"
[rules]
disable = ["E501"] # We use 120-char lines
[[rules.custom]]
code = "acme/ticket-ref"
pattern = "TODO(?!.*\\[ACME-\\d+\\])"
message = "TODO comments must reference a JIRA ticket [ACME-XXX]"
severity = "warning"
[rust]
max_complexity = 12
[python]
max_complexity = 10
```
### Security Rules Plugin
```toml
# linthis-plugin.toml
[plugin]
name = "security-rules"
version = "1.0.0"
description = "Security-focused lint rules"
[config]
path = "security.toml"
```
```toml
# security.toml
[[rules.custom]]
code = "sec/no-eval"
pattern = "\\beval\\s*\\("
message = "Avoid eval() - potential code injection vulnerability"
severity = "error"
languages = ["javascript", "typescript", "python"]
[[rules.custom]]
code = "sec/no-hardcoded-secret"
severity = "error"
[[rules.custom]]
code = "sec/no-http"
pattern = "http://"
message = "Use HTTPS instead of HTTP"
severity = "warning"
```
## See It in Action
Watch the [Plugin System video tutorial](../getting-started/videos.md#episode-3-plugin-system) for a 20-second demo.
## Troubleshooting
### Plugin not loading
1. Check `linthis plugin list` to see installed plugins
2. Run `linthis plugin init` to re-fetch
3. Verify the Git URL is accessible
4. Check for manifest errors in `linthis-plugin.toml`
### Configuration not applied
1. Check merge order (later plugins override earlier)
2. Verify the config file path in manifest
3. Run with `--verbose` to see config loading
### Version conflicts
If you see version compatibility errors:
1. Update linthis to the required version
2. Or use an older plugin version with `ref = "v1.0.0"`