Halluacinate (Lua)
Halluacinate lets you teach FreeChaOS new tricks without recompiling. Drop a Lua script into the right folder and the LLM can call it as a tool.
Quick start
Section titled “Quick start”Create a script at ~/.config/chaos/scripts/project_info.lua:
chaos.tool({ name = "project_info", description = "Summarize the current session context", input_schema = { type = "object", properties = {} }, handler = function(_args) return "cwd=" .. chaos.cwd .. " provider=" .. chaos.provider end})Start a session. The LLM now has a project_info tool it can call.
Halluacinate also owns the TUI status line. ChaOS ships a built-in Lua renderer, and you can override it by registering your own chaos.statusline(...) script. The built-in script loads first, then user scripts, then project scripts - the last registered statusline wins.
Script locations
Section titled “Script locations”Scripts are loaded automatically on session startup from two places:
| Layer | Path | Priority |
|---|---|---|
| User | ~/.config/chaos/scripts/ |
Lower |
| Project | .chaos/scripts/ (relative to working directory) |
Higher |
Project scripts load after user scripts and can override tools with the same name. Scripts load in alphabetical order within each layer. Only .lua files are loaded; WASM support is planned.
Session context
Section titled “Session context”Read-only information about the current session:
chaos.session_id -- unique conversation IDchaos.cwd -- working directorychaos.provider -- provider name (e.g. "anthropic", "openai")Registering tools
Section titled “Registering tools”chaos.tool({ name = "tool_name", description = "What it does - the LLM reads this", input_schema = { type = "object", properties = { param = { type = "string", description = "..." } }, required = { "param" } }, handler = function(args) -- args is a Lua table with parsed parameters -- return a string with the result return "done" end})The input_schema follows JSON Schema. The LLM uses it to understand what parameters to pass.
React to events during the session:
chaos.on("event_name", function(payload) -- payload is a Lua table chaos.log.info("event fired")end)Logging
Section titled “Logging”chaos.log.info("message")chaos.log.warn("message")chaos.log.debug("message")Logs go to the FreeChaOS log file (~/.chaos/log/).
Status line
Section titled “Status line”Override the default TUI status line:
chaos.statusline(function(ctx) return { { text = ctx.model, bold = true }, { text = " · " }, { text = tostring(ctx.context.remaining_pct) .. "% left" }, { text = " · " }, { text = ctx.cwd_display or ctx.cwd }, }end)Place a statusline script in either script layer (~/.config/chaos/scripts/statusline.lua or .chaos/scripts/statusline.lua); the project copy overrides the user copy because project scripts load later.
The built-in renderer is a Doom-style HUD. By default it shows:
HUDso it is obviously not the old Rust footerHP, where remaining context is treated as health and color-codedCRITwhen context health gets dangerously lowWPN, showing the active model and reasoning effortMAP, using the current branch, project root, or directoryARM, showing sandbox and approval postureCTX, showing effective context load above the fixed prompt baselineAMMO, showing last-response output with optional context growthDIR, when it adds extra path context beyondMAP
The ctx table includes ctx.model, ctx.reasoning_effort, ctx.provider, ctx.branch, ctx.cwd, ctx.cwd_display, ctx.project_root, ctx.approval, ctx.sandbox, ctx.version, ctx.session_id, ctx.context.{remaining_pct,used_pct,window_size}, ctx.tokens.* (including context_effective, last_output, and the legacy cumulative used/input/output counters), ctx.five_hour, and ctx.weekly. For statusline UX, prefer the explicit fields like ctx.tokens.context_effective and ctx.tokens.last_output over the legacy counters.
Sandbox
Section titled “Sandbox”Scripts run in a sandbox. Available standard libraries:
string,table,math,utf8,coroutineassert,error,ipairs,next,pairs,pcall,print,select,tonumber,tostring,type,unpack,xpcall
Blocked:
os,io,debug,packagerequire,load,loadfile,dofile,collectgarbage
Each script gets its own isolated environment - scripts cannot interfere with each other.
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Memory per script | 8 MiB |
| Execution time per tool call | 10 seconds |
| Batch load timeout | 30 seconds |
If a script exceeds these limits, the call fails and FreeChaOS continues without it.
Examples
Section titled “Examples”String utility
Section titled “String utility”chaos.tool({ name = "reverse_string", description = "Reverse a string", input_schema = { type = "object", properties = { text = { type = "string", description = "Text to reverse" } }, required = { "text" } }, handler = function(args) return string.reverse(args.text) end})Logging hook
Section titled “Logging hook”chaos.on("tool_call", function(payload) chaos.log.info("Tool called: " .. tostring(payload.name))end)Troubleshooting
Section titled “Troubleshooting”Tool doesn’t appear - Check the file is in ~/.config/chaos/scripts/ or .chaos/scripts/ and has a .lua extension. Check logs for load errors.
Script fails silently - Look at ~/.chaos/log/ for error details. Common causes: syntax errors, exceeding memory or time limits.
Tool returns wrong result - The handler must return a string. If you return a table or nil, the result will be empty.