> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lyzr.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugins

> Extend GitAgent with custom skill types, output processors, and runtime integrations.

Plugins extend the GitAgent runtime itself. While skills add capabilities to an agent, plugins add capabilities to the runtime — new skill discovery methods, output processors, authentication providers, memory backends, and more.

## Plugin types

| Plugin type       | What it extends                                          |
| ----------------- | -------------------------------------------------------- |
| `SkillLoader`     | How skills are discovered and loaded                     |
| `OutputProcessor` | How skill outputs are processed before the LLM sees them |
| `AuthProvider`    | Authentication for outbound HTTP requests in skills      |
| `MemoryBackend`   | Custom memory storage (database, API, etc.)              |
| `Tracer`          | Custom telemetry export                                  |

## Writing a plugin

Plugins live in the `plugins/` directory. Each plugin is a Python class that inherits from the appropriate base class:

```python theme={null}
# plugins/my_output_processor.py
from gitagent.plugins import OutputProcessor

class TruncatePlugin(OutputProcessor):
    """Truncate long skill outputs to keep context size manageable."""

    def __init__(self, max_chars: int = 2000):
        self.max_chars = max_chars

    def process(self, skill_name: str, output: any) -> any:
        if isinstance(output, str) and len(output) > self.max_chars:
            return output[:self.max_chars] + f"\n\n[Truncated — {len(output)} total chars]"
        return output
```

Register the plugin in `agent.yaml`:

```yaml theme={null}
plugins:
  - class: TruncatePlugin
    config:
      max_chars: 3000
```

## `SkillLoader` plugin

Load skills from non-standard sources (a database, a remote URL, a compiled package):

```python theme={null}
# plugins/db_skill_loader.py
from gitagent.plugins import SkillLoader
from gitagent import skill
import json

class DatabaseSkillLoader(SkillLoader):
    """Load skill definitions stored in a database."""

    def load_skills(self) -> list:
        skills = []
        for row in db.query("SELECT * FROM agent_skills WHERE active = true"):
            # Dynamically create skill functions
            fn = self._make_skill_function(row)
            skills.append(fn)
        return skills

    def _make_skill_function(self, row):
        @skill(name=row["name"], description=row["description"])
        def dynamic_skill(**kwargs):
            return call_api(row["endpoint"], kwargs)
        return dynamic_skill
```

## `AuthProvider` plugin

Inject authentication into outbound HTTP requests made by skills:

```python theme={null}
# plugins/bearer_auth.py
from gitagent.plugins import AuthProvider
import os

class BearerAuthPlugin(AuthProvider):
    """Inject a bearer token into requests to internal APIs."""

    def get_headers(self, url: str) -> dict:
        if "internal.example.com" in url:
            return {"Authorization": f"Bearer {os.environ['INTERNAL_API_TOKEN']}"}
        return {}
```

Register in `agent.yaml`:

```yaml theme={null}
plugins:
  - class: BearerAuthPlugin
```

The `http_request` built-in tool automatically calls `get_headers()` before each request.

## `MemoryBackend` plugin

Implement a custom memory storage layer:

```python theme={null}
# plugins/postgres_memory.py
from gitagent.plugins import MemoryBackend
import psycopg2

class PostgresMemory(MemoryBackend):
    def __init__(self, dsn: str):
        self.conn = psycopg2.connect(dsn)

    def read(self, key: str) -> str | None:
        with self.conn.cursor() as cur:
            cur.execute("SELECT value FROM agent_memory WHERE key = %s", (key,))
            row = cur.fetchone()
            return row[0] if row else None

    def write(self, key: str, value: str) -> None:
        with self.conn.cursor() as cur:
            cur.execute(
                "INSERT INTO agent_memory (key, value) VALUES (%s, %s) "
                "ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value",
                (key, value)
            )
        self.conn.commit()

    def delete(self, key: str) -> None:
        with self.conn.cursor() as cur:
            cur.execute("DELETE FROM agent_memory WHERE key = %s", (key,))
        self.conn.commit()
```

Register in `agent.yaml`:

```yaml theme={null}
memory:
  provider: custom
  plugin: PostgresMemory
  config:
    dsn: ${DATABASE_URL}
```

## `Tracer` plugin

Send telemetry to a custom backend:

```python theme={null}
# plugins/my_tracer.py
from gitagent.plugins import Tracer

class SplunkTracer(Tracer):
    def on_run_start(self, run_id: str, input: str): ...
    def on_skill_call(self, run_id: str, skill_name: str, input: dict): ...
    def on_skill_result(self, run_id: str, skill_name: str, output: any): ...
    def on_run_end(self, run_id: str, output: str, duration_ms: int): ...
```

## Plugin configuration

Full `agent.yaml` plugin configuration:

```yaml theme={null}
plugins:
  - class: TruncatePlugin          # local file: plugins/truncate_plugin.py
    config:
      max_chars: 3000

  - class: gitagent_slack.SlackNotifier  # installed package
    config:
      webhook_url: ${SLACK_WEBHOOK}
      notify_on: [run_complete, run_error]
```

Plugins can be:

* Local files in `plugins/` (referenced by class name)
* Installed Python packages (referenced by fully-qualified dotted path)

## Publishing a plugin

Plugins can be packaged and published to PyPI for reuse across projects:

```python theme={null}
# setup.py (or pyproject.toml)
entry_points = {
    "gitagent.plugins": [
        "my-plugin = my_package.plugin:MyPlugin"
    ]
}
```

Once installed, reference by entry point name in `agent.yaml`:

```yaml theme={null}
plugins:
  - class: my-plugin
    config: ...
```
