Navigation
Copy page

Write Your First Lisp Extension

On this page

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.
  • Understand why a running kli can change itself this way: see The live image.
  • See the full defextension grammar, every contribution kind, and the author DSL: see Extension reference.