# Loading and Managing Extensions


This page covers how a Lisp extension reaches a running kli and how you turn one on or off without restarting. For writing the extension itself, see [Write your first Lisp extension](/kli/extend/lisp-extensions/write-your-first); for the full `defextension` grammar, see [Extension reference](/kli/extend/lisp-extensions/anatomy).

An extension comes from a file or directory on disk. kli reads that source, indexes what it declares as an available extension, and installs the enabled ones into the session. Four things decide which extensions a session has: where kli looks, what `config.json` says, the `--extension` flag, and the in-session commands.

## Drop a file in a discovery directory

kli scans two directories on every launch, in this order:

1. `~/.config/kli/extensions/` — loaded in every session.
2. `<project>/.kli/extensions/` — loaded only when kli runs in that project. Project files extend the global set; they do not replace it.

A directory that does not exist is skipped, not an error. To add an extension, write its `.lisp` file (or its unit directory) into one of these and start kli, or run `/reload` in a session already open.

## Load a one-off file with --extension

To load a file or directory that is not in a discovery directory, name it on the command line:

```
kli --extension ./scratch/greet.lisp
```

The flag is repeatable, and each value is either a file or a directory:

```
kli --extension ./scratch/greet.lisp --extension ./team-extensions/
```

A path that is a directory is discovered as a unit (or a group of units); any other path loads as a single file. Extensions named with `--extension` are added on top of whatever the discovery directories and `config.json` already contribute, for that one session only. Nothing is written to disk and the next launch forgets them.

## Add extra roots in config.json

`~/.config/kli/config.json` is an optional file. Three keys control loading:

| Key | Type | Effect |
|-----|------|--------|
| `enabled` | array of names | Force these extensions on, overriding their default. |
| `disabled` | array of names | Force these extensions off. |
| `extension-dirs` | array of paths | Extra directories to scan, in addition to the two discovery directories. |

```json
{
  "enabled": ["greet"],
  "disabled": ["noisy-extension"],
  "extension-dirs": ["/srv/shared/kli-extensions"]
}
```

Names are matched case-insensitively, so `"Greet"` and `"greet"` name the same extension. A directory under `extension-dirs` that does not exist is skipped. If `config.json` is missing or malformed, kli warns and proceeds as though it were absent rather than failing to start.

## Which extensions are enabled

An indexed extension is installed unless something turns it off. Settle a name by walking these checks in order and taking the first that names it:

1. The active profile's disable list (if a profile is active) turns it off.
2. The active profile's enable list turns it on.
3. `disabled` in `config.json` turns it off.
4. `enabled` in `config.json` turns it on.
5. The extension's own `:autoload` metadata, if it set one.
6. Otherwise, on.

To override an extension that ships off by default, add its name to `enabled`. To override an extension that ships on, add it to `disabled`. A profile wins over `config.json`, so a profile's lists can flip either way for that profile only. For profiles, see [Profiles](/kli/config/profiles).

## Reload after an edit

In a running session, `/reload` re-reads every extension file on disk:

```
/reload
```

It retracts every installed user extension, clears the indexed registry and the diagnostics from the previous pass, re-indexes from the current files, and installs the enabled set again. An edit to a file takes effect in place, with no rebuild and no restart. kli replies with the list of user extensions it now knows about and their state.

Loading is fail-soft: a file that errors warns and is isolated under a `[diagnostics]` entry while every other extension keeps working. Fix the file and run `/reload` again.

## Toggle one extension live

`/enable` and `/disable` switch a single extension without touching disk:

```
/enable greet
```

`/enable NAME` installs an indexed extension that is currently off. kli replies `Enabled greet.`, or `greet is already enabled.` if it was already on, or `No such extension: greet.` if no extension by that name was indexed.

```
/disable greet
```

`/disable NAME` retracts an installed extension from the session. kli replies `Disabled greet.`, or `greet is not enabled.` if it was not installed. The file stays on disk, so a later `/reload` or the next session brings the extension back unless `config.json` disables it.

## List what is available

`/extensions` reports every indexed extension and whether it is currently installed:

```
/extensions
```

Each line is marked `[enabled]` or `[disabled]`. When no user extensions were found, kli says so. To see which files failed to load, run `/reload`, which appends a `[diagnostics]` line for each failed unit; `/extensions` lists state only.
