Skip to content

Scripting Languages

Crucible uses Lua for plugins, tools, and hooks. Fennel (a Lisp that compiles to Lua) is also supported.

LanguageBest ForKey Feature
LuaGeneral use, LLM-generated codeFamiliar syntax, simple
FennelPower users wanting macrosLisp syntax, compiles to Lua

Both languages can:

  • Define MCP tools via annotations
  • Register event hooks
  • Access the Crucible API (search, notes, graph)
  • Execute shell commands (with policy controls)

Lua is a simple, embeddable scripting language with massive adoption. Crucible uses Luau (Lua with gradual types) for enhanced tooling.

Strengths:

  • Familiar syntax (if you know JavaScript/Python)
  • LLM-friendly (models generate excellent Lua)
  • Gradual types for documentation
  • Simple and easy to debug

See Language Basics for syntax and examples.

Fennel is a Lisp that compiles to Lua. It provides:

  • S-expression syntax
  • Compile-time macros
  • Full Lua interoperability
  • Pattern matching

Fennel files (.fnl) are compiled to Lua at load time, so they have the same runtime characteristics.

  • You want simple, readable code
  • LLMs will generate your plugins
  • You’re prototyping quickly
  • You prefer familiar syntax
  • You love Lisp
  • You want compile-time macros
  • You’re building DSLs
  • You prefer s-expressions

Place files in:

~/.config/crucible/plugins/ # Global personal
KILN/.crucible/plugins/ # Kiln personal (gitignored)
KILN/plugins/ # Kiln shared (version-controlled)

File extensions determine the runtime:

  • .lua — Lua
  • .fnl — Fennel (compiles to Lua)

Crucible also loads configuration from ~/.config/crucible/init.lua. This allows customizing the TUI, defining keybindings, and more.

See Configuration for details.

Both Lua and Fennel can build TUI components using the Oil (Obvious Interface Language) API. Oil provides a functional, React-like model where components are functions that return node trees.

-- Lua
local view = cru.oil.col({ gap = 1 },
cru.oil.text("Hello", { bold = true }),
cru.oil.when(loading, cru.oil.spinner())
)
;; Fennel
(local oil (require :oil))
(oil.col {:gap 1}
(oil.text "Hello" {:bold true})
(oil.when loading (oil.spinner)))

See Scripted UI for the full Oil component reference.