From 9cd3f87dd63bff27acbe3966e90f3b689e898b93 Mon Sep 17 00:00:00 2001 From: t Date: Sat, 11 Jul 2026 14:06:25 -0600 Subject: Import Pantograph website project Add the standalone Pantograph documentation site, including its assets, shared styling, and bundled design-system artifacts. --- pantograph/lua.html | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 pantograph/lua.html (limited to 'pantograph/lua.html') diff --git a/pantograph/lua.html b/pantograph/lua.html new file mode 100644 index 0000000..ace492f --- /dev/null +++ b/pantograph/lua.html @@ -0,0 +1,74 @@ + + + + + +panto — Lua extensions + + + + + +
+
+
+ +panto/docs +CLI +
+ + +
+
+ +
+

Reference

Lua extensions

require("panto")

panto's standard tools are Lua extensions, and your own extensions use the same loader. A source file returns one entry, one tool declaration, or a list of them; activation then registers tools, slash commands, and UI-event handlers into the live session.

Overview

Extension source files are evaluated first and activated later. A file must return one of these shapes:

{ name, activate }
A named entry whose activate() function performs registration when the entry survives policy and shadowing.
{ name, description, schema, handler }
Sugar form for a single tool entry. Activation registers it as a tool automatically.
{ ... , ... }
A list of entries or tool declarations returned from one file.

Inside activate(), use local panto = require("panto") and the panto.ext table:

register_tool
Register a model-facing tool.
register_command
Register a slash command.
on / emit
Subscribe to transcript/UI lifecycle events or fire custom ones.

Loading extensions

panto scans both extensions/ and tools/ directories at each layer:

LayerLocation
base$XDG_DATA_HOME/panto/agent/{extensions,tools}
user$XDG_CONFIG_HOME/panto/{extensions,tools} · else ~/.config/panto/{extensions,tools}
project./.panto/{extensions,tools}

Additional sources can come from [extensions] paths and [extensions] rocks. Cross-layer precedence is project > user > base. Within one layer, canonical directories beat extra paths, which beat rocks. Identity is the returned entry's name, not the filename.

Eval is side-effect free

Every candidate file is evaluated before policy and shadowing are resolved, including entries that will later be denied. Put side effects in activate(), not at top level.

register_tool

The simplest way to define a tool is to return the sugar form directly:

.panto/tools/echo.lualua
return {
+  name = "echo",
+  description = "Echo back the given message.",
+  schema = {
+    type = "object",
+    properties = {
+      message = { type = "string" },
+    },
+    required = { "message" },
+  },
+  handler = function(input)
+    return "echo: " .. input.message
+  end,
+}

Use register_tool inside activate() when you want one entry to register multiple tools or commands together.

register_command

Slash commands are typically registered from an explicit entry:

.panto/extensions/greet.lualua
return {
+  name = "greet",
+  activate = function()
+    local panto = require("panto")
+    panto.ext.register_command {
+      name = "greet",
+      description = "Print a greeting.",
+      handler = function(args)
+        local who = args ~= "" and args or "world"
+        io.write("\n[greet] hello, " .. who .. "!\n")
+      end,
+    }
+  end,
+}

Events: on & emit

panto.ext.on(name, handler) subscribes to transcript/UI lifecycle events. The handler receives an event userdata with snake-case methods get_component() and set_component(), plus payload fields such as tool_name, delta, text, id, input, output, cwd, and model.

Event familyNames
tooltool, tool_details, tool_delta, tool_call_complete, tool_result, tool_collapse
thinkingthinking, thinking_delta, thinking_complete
assistant textassistant_text, assistant_text_delta, assistant_text_complete
single-shotuser_message, session_start, compaction
customwhatever you emit via panto.ext.emit

A few payload fields are writable during dispatch: user_message.text, tool_call_complete.input (assign a Lua table; panto serializes it to JSON), and tool_result.output.

.panto/extensions/skill-badge.lualua
return {
+  name = "skill-badge",
+  activate = function()
+    local panto = require("panto")
+    panto.ext.on("tool_details", function(e)
+      if e.tool_name ~= "skill" then return end
+      local inner = e:get_component()
+      e:set_component({
+        render = function(self, width)
+          local lines = inner:render(width)
+          lines[#lines + 1] = "▸ running skill"
+          return lines
+        end,
+      })
+    end)
+  end,
+}

Lua components

A Lua component is a table passed to set_component(). The required method is render(self, width), which returns an array of strings. handleInput(self, data) is optional. Differential repaint is cache-derived automatically; you do not need to implement firstLineChanged yourself.

Runtime & luarocks

panto embeds a pinned Lua and luarocks runtime and ships luv as its core async dependency. Extra rocks may be loaded as extension sources via [extensions] rocks and installed with panto update, or added manually with panto lua.

shellbash
panto update
+panto lua -e 'arg[0]="luarocks"; require("luarocks.cmd").run_command("install","lpeg")'
+ +
+
+ + + -- cgit v1.3