Run Commands and Eval Lisp
On this page
kli has two tools for executing code in a session: bash runs a shell command in a child process, and eval evaluates Common Lisp forms inside the running kli image. The agent calls them while it works, and you can call either one directly with a slash command.
Both tools are gated behind capabilities. They run only when the session grants them: bash needs process/exec and eval needs image/eval. With the default settings (no capabilities key) every tool is allowed. See Restrict Tools With Capabilities to deny one.
Run a shell command
Type /bash followed by the command:
/bash ls -la srcEverything after /bash is the command line, passed to sh -c. So pipes, redirects, globs, and && work as written:
/bash grep -rn TODO src | head -20The tool returns stdout on success. When the command exits non-zero, the result is marked an error and includes stderr. An empty result means the command produced no output and exited zero.
Set the working directory or pass stdin
The agent can run a command in a specific directory or feed it input. These are tool parameters, not shell syntax, so the agent supplies them on the tool call rather than you typing them after /bash. The bash tool accepts:
| Parameter | Required | Effect |
|---|---|---|
command | yes | The command line to run. |
directory | no | Working directory for the child process. |
input | no | Text written to the command's stdin. |
shell | no |
Shell to invoke. Defaults to sh. |
From the /bash slash command, only the command line is available; the rest take their defaults. To change the working directory yourself, cd inside the command itself.
Evaluate Common Lisp
Type /eval followed by one or more forms:
/eval (+ 1 2 3)The forms are read and evaluated in order. The result is whatever the forms printed, followed by the value of the last form. Several forms in one call run left to right:
/eval (defparameter *x* 10) (* *x* *x*)Forms read and evaluate in the CL-USER package by default. The agent can target another package through the tool's package parameter; unqualified symbols then intern there. Values print under bounded printer control: *print-length* is 100, *print-level* is 20, and *print-circle* is on, so a long or circular value prints a bounded representation instead of running away.
The forms run in the same image kli runs in, so they reach live state and can inspect or change kli mid-session. For what that image is and why it matters, see The Live Image.
Stay inside the timeouts
Both tools time out at 30 seconds by default. The hard maximum is 300 seconds; a longer request is clamped to it. There is no way to extend a single call past 300 seconds.
When a bash command times out, kli kills its whole process group (SIGTERM, then SIGKILL) and returns the output captured so far, marked as a timeout error. When an eval form times out, kli interrupts the evaluating thread. An interrupted form can leave image state partially modified, since it stops wherever it was, so the result says so.
To run something longer than 300 seconds, start it in the background from a bash command and poll its progress with later commands, rather than waiting inside one call.
Know the output cap
Each tool caps its captured output at 1 MiB (1,048,576 characters):
bashcaps stdout and stderr at 1 MiB each. Past the cap the stream is truncated and the result notes[stdout truncated at ... characters].evalcaps total output at 1 MiB across everything the forms print. The cap applies as output is written, so even a non-terminating printing loop stops adding to the result at the cap.
When you expect a large result, narrow it before it reaches the tool: pipe a bash command through head, tail, or grep, and have eval return a count or a slice rather than a whole collection.
Commands bash refuses
The bash tool runs to completion and returns captured output; it has no terminal to drive. So it refuses commands whose first word is an interactive program and returns an error instead of hanging:
vi vim nvim nano emacs
less more man
top htop watch
ssh mosh
tmux screenThe check looks past leading variable assignments and wrappers (env, command, exec, time, sudo, doas) to find the real command, so sudo vim file is refused too. Reach for non-interactive equivalents: read a file with cat instead of less, search with grep instead of opening an editor, run a remote command through whatever your environment exposes rather than an interactive ssh session.