Navigation
Copy page

Skill Examples

On this page

Each section below is a complete SKILL.md you can drop into a skills directory and use. Pick the one whose shape matches what you want, copy it, and edit the frontmatter and body. Put the file at <repo>/.kli/skills/<name>/SKILL.md for a skill that belongs to one project, or ~/.config/kli/skills/<name>/SKILL.md for one you want in every session. Leave name out and kli uses the folder name; set it to something other than the folder name and kli keeps your name but warns.

For the discovery rules these examples rely on, see the skills reference. For why skills exist and how the model reaches them, see Skills.

A domain-knowledge skill

This is the common case: a procedure or house rule the model should follow on its own whenever a matching task comes up, without you naming it. The body is the knowledge; the description is what the model matches your request against, so it states plainly what the skill covers and when it applies.

Put this at <repo>/.kli/skills/writing-migrations/SKILL.md:

markdown
---
name: writing-migrations
description: House rules for database migrations in this repo - file naming, the up/down structure, and the backfill-then-constraint ordering. Use when writing, reviewing, or editing a schema migration.
---

# Writing migrations

Every migration is reversible and lands as one file under `db/migrate/`.

## File and naming

- One change per file. Name it `<utc-timestamp>_<verb>_<subject>.sql`,
  e.g. `20260619T0930_add_email_to_users.sql`.
- Each file has an `-- up` section and a `-- down` section. The down section
  must return the schema to its prior state exactly.

## Ordering rules

- Add a column nullable first, backfill it in a separate statement, then add
  the `NOT NULL` constraint. Never add a non-null column with a default to a
  large table in one step.
- Create an index `CONCURRENTLY`. A plain `CREATE INDEX` locks the table.

## Before you finish

- Confirm the down section drops exactly what the up section created.
- Note the expected row count touched by any backfill in a comment above it.

The model loads this only when the description matches the task in front of it, so the body can be as long as the procedure needs.

A tool-recipe skill

A tool recipe is a step list for using a command-line tool the right way: the flags that matter, the order to run things in, what to check after. It packages operational knowledge the model would otherwise have to guess at.

Put this at ~/.config/kli/skills/profiling-with-perf/SKILL.md:

markdown
---
name: profiling-with-perf
description: Recipe for CPU-profiling a running process with perf and turning the result into a flamegraph. Use when asked to profile, find a hot path, or explain where time goes in a process.
---

# Profiling with perf

Sample a running process, fold the stacks, and render a flamegraph.

1. Find the target pid: `pgrep -f <process-name>`.
2. Record for ten seconds at 99 Hz, capturing call graphs:

   ```sh
   perf record -F 99 -p <pid> -g -- sleep 10
   ```

3. Collapse the samples and render:

   ```sh
   perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
   ```

4. Read the widest frames in `flame.svg` from the bottom up. The widest box
   that is not a scheduler or idle frame is the hot path.

If `perf record` reports no samples, the process is mostly idle or blocked on
I/O; switch to `perf record -e sched:sched_switch` to see where it waits.

A tool recipe often pairs with companion files. If the body says to run scripts/setup.sh, put that script beside SKILL.md in the folder; kli tells the model the folder location and that references resolve against it.

A sigil-invoked skill

You can pull any skill into a message yourself by writing $<name> in the prompt. kli expands that skill's body inline before sending, so a sigil is the way to reach for a skill on the spot. To make a skill only reachable that way and by command, and keep it out of the model's automatic matching, add disable-model-invocation: true to the frontmatter. The skill then never shows up in the advertised list, so the model will not load it on its own; you invoke it with $<name> or /skill:<name>.

Put this at ~/.config/kli/skills/explain-like-staff/SKILL.md:

markdown
---
name: explain-like-staff
description: Rewrite an explanation for a staff-level engineer - lead with the tradeoff, drop the basics, name the failure modes. Invoke by hand when you want this lens applied.
disable-model-invocation: true
---

# Explain like staff

Rewrite the explanation that follows for a staff-level engineer.

- Open with the decision and its tradeoff, not with background.
- Assume fluency in the domain. Cut definitions of standard terms.
- Name the failure modes and the conditions that trigger each one.
- State what you would measure to know the choice was right.

In a message you would then write:

$explain-like-staff Here is the draft of the caching section: ...

kli prepends the skill body to your message before it reaches the model. A $name that matches no discovered skill stays as plain text, so ordinary prose containing a dollar sign is untouched.

A skill that shadows a built-in

kli ships a small set of built-in skills, and they sit last in discovery order. Any skill of yours that has the same name as a built-in wins, because the first skill found under that name is the one kept and your directories are searched first. Give your skill the built-in's exact name to replace its body with yours.

To shadow the built-in creating-extensions skill with your own house version, put this at ~/.config/kli/skills/creating-extensions/SKILL.md:

markdown
---
name: creating-extensions
description: Author kli user extensions in Common Lisp - commands, event handlers, and tools loaded from ~/.config/kli/extensions/ with hot reload via /reload. Use when asked to create, modify, or debug a kli extension. Adds our team's conventions on top of the basics.
---

# Creating kli extensions (team conventions)

Follow the standard extension shape, with these additions for our codebase.

- Name every extension `<team>-<purpose>`, e.g. `payments-deploy-guard`.
- Project-specific extensions go in `<repo>/.kli/extensions/`. Only
  cross-project tools go in `~/.config/kli/extensions/`.
- Every `effect` contribution must pair an installer with a real retractor.
  Reverting must drain exactly what installing created.

## The minimal shape

```lisp
(defextension payments-greet
  (:provides
   (command "pay-greet"
     :description "Greet the payments on-call."
     :arguments '(:tail :name)
     :handler (lambda (command arguments context &key call-id on-update)
                (declare (ignore command context call-id on-update))
                (reply (format nil "Hi ~A, you are on call."
                               (or (rest-arg arguments) "there")))))))

After writing the file, run /reload, then check /extensions shows it enabled. ```

Because your file is found before the shipped one, the model and the /skill:creating-extensions command both load your version. If you remove your copy, the built-in returns on the next discovery pass. Naming must be exact: a different name does not shadow, it adds a second skill.

Verify a skill loaded

After you add or edit any of these files, kli re-discovers skills on the next session start. Confirm the result by invoking the skill yourself with /skill:<name> — every discovered skill registers under that command, so a skill that runs there is one kli found and parsed. A skill with no description is dropped silently, so if /skill:<name> is missing, check the frontmatter first.