# Write Your First Lisp Extension


By the end of this page you will have a working slash command, `/greet`, that you wrote, loaded into a running kli, edited, reloaded without restarting, and switched off. You will do all of it against one live session.

You write an extension as a Common Lisp file on disk. kli reads the file and installs the command it declares. There is no build step and no restart.

## Create the file

Extensions in `~/.config/kli/extensions/` load in every session. Create that directory if it does not exist, then write a file named `greet.lisp` inside it.

```lisp
(defextension greet
  (:provides
   (command "greet"
     :description "Greet someone by name."
     :arguments '(:tail :name)
     :handler (lambda (command arguments context &key call-id on-update)
                (declare (ignore command context call-id on-update))
                (reply (format nil "Hello ~A!"
                               (or (rest-arg arguments) "world")))))))
```

That is the whole extension. Reading it line by line:

- `defextension greet` names the extension `greet`.
- `(command "greet" ...)` declares the slash command. The string is the name you type after the slash.
- `:arguments '(:tail :name)` captures everything typed after `/greet` as one free-text tail.
- The handler signature `(command arguments context &key call-id on-update)` is fixed; declare-ignore the parameters you do not use.
- `(rest-arg arguments)` returns that captured tail, or `nil` when you typed nothing after the command name.
- `(reply text)` builds the result kli shows.

A single `.lisp` file loads in the `kli/author` package, so `defextension`, `command`, `reply`, and `rest-arg` are available unqualified. You import nothing.

## Load it into the running session

Switch to your kli session and run:

```
/reload
```

`/reload` re-reads every extension file on disk and installs what it finds. kli prints the list of user extensions it now knows about; `greet` is among them, marked enabled.

Now run the command:

```
/greet Ada
```

kli answers `Hello Ada!`. Run it with no argument:

```
/greet
```

kli answers `Hello world!`, because `(rest-arg arguments)` returned `nil` and the `or` fell through to the default.

## Edit it and reload live

You can change the command against the same running session. Open `greet.lisp` and change the greeting.

```lisp
(reply (format nil "Welcome, ~A."
               (or (rest-arg arguments) "stranger")))
```

Save the file, return to kli, and run `/reload` again. `/reload` retracts every installed user extension, re-reads the files, and reinstalls them, so your edit replaces the old command in place. Run `/greet Ada` and kli now answers `Welcome, Ada.`.

If a file has an error, loading is fail-soft: kli warns about that one file and isolates it while every other extension keeps working. Fix the file and `/reload` again.

## Switch it off

To take the command out of the session without deleting the file, disable it by name:

```
/disable greet
```

kli answers `Disabled greet.`, and `/greet` no longer runs. The file stays on disk, so a later `/reload` (or the next session) brings it back. To list what is available and which extensions are currently enabled, run:

```
/extensions
```

## What you did

You wrote a slash command in a Lisp file, loaded it with `/reload`, edited it and reloaded it against the same live session, and disabled it with `/disable`. Edit, `/reload`, repeat: that is how you build a kli extension.

From here:

- Add a command that reacts to session events, or one the model can call as a tool: see [Lisp extensions](/kli/extend/lisp-extensions).
- Understand why a running kli can change itself this way: see [The live image](/kli/concepts/the-live-image).
- See the full `defextension` grammar, every contribution kind, and the author DSL: see [Extension reference](/kli/extend/lisp-extensions/anatomy).
