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

```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"
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:

```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"
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](../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"`
