Skip to content

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

# 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:

[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:

# 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"
pattern = "FIXME|XXX"
message = "FIXME comments must be resolved before merge"
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

mkdir my-linthis-plugin
cd my-linthis-plugin
git init

Step 2: Create Manifest

Create linthis-plugin.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:

# 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:

# In your project's .linthis/config.toml
[plugins]
sources = [
    { name = "local-test", url = "file:///path/to/my-linthis-plugin" }
]

Then run:

linthis plugin init
linthis -c

Step 5: Publish

Push to GitHub or any Git host:

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:

[plugins]
sources = [
    { name = "my-plugin", url = "https://github.com/username/my-linthis-plugin.git" }
]

Or pin to a specific version:

[plugins]
sources = [
    { name = "my-plugin", url = "https://github.com/username/my-linthis-plugin.git", ref = "v1.0.0" }
]

Plugin Commands

# 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:

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:

# Prevent debug code from being committed
[[rules.custom]]
code = "org/no-debug"
pattern = "debugger|console\\.debug"
message = "Debug code should not be committed"

3. Set Minimum Version

If your plugin uses features from a specific linthis version:

[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

# linthis-plugin.toml
[plugin]
name = "acme-style"
version = "2.0.0"
description = "ACME Corp coding standards"

[config]
path = "config.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

# linthis-plugin.toml
[plugin]
name = "security-rules"
version = "1.0.0"
description = "Security-focused lint rules"

[config]
path = "security.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"
pattern = "(password|secret|api_key|token)\\s*=\\s*['\"][^'\"]+['\"]"
message = "Potential hardcoded secret detected"
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 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"