diff options
| author | t <t@tjp.lol> | 2026-07-06 15:00:37 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-07-06 15:00:48 -0600 |
| commit | b350e4dee2412e450b3844061f5c1bc2ec995923 (patch) | |
| tree | c518b3c14af460f31c624f9ba84902713cb8a117 | |
| parent | c21a4ebd001ca305d862b5390f090f2ef14163cf (diff) | |
| -rw-r--r-- | cli.html | 9 | ||||
| -rw-r--r-- | configuration.html | 53 | ||||
| -rw-r--r-- | index.html | 16 | ||||
| -rw-r--r-- | keybindings.html | 4 | ||||
| -rw-r--r-- | lua.html | 56 |
5 files changed, 58 insertions, 80 deletions
@@ -20,13 +20,10 @@ <div class="pd-header__right"><a class="pd-ghlink" href="https://code.tjp.lol/pantograph.git/" target="_blank" rel="noreferrer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 .5 5 .5 5 .5c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 7.5c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg> Source</a></div> </header> <div class="pd-shell"> -<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link pd-navsec__link--active">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Tools & extensions</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> +<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link pd-navsec__link--active">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Extensions policy</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> <div class="pd-scrim"></div> -<main class="pd-main"><article class="pd-doc" data-screen-label="Command line"><p class="panto-eyebrow pd-eyebrow">Reference</p><h1 class="pd-doc__h1">Command line</h1><p class="pd-lead"><code>panto</code> dispatches on its first argument. A handful of subcommands (<code>lua</code>, <code>bootstrap</code>, <code>sessions</code>, <code>models</code>, <code>auth</code>, <code>help</code>) short out before the agent loop; anything else — or nothing — starts the agent in the current directory.</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Command</th><th>Does</th></tr></thead><tbody><tr><td class="mono">panto</td><td>Start a new conversation in the current directory.</td></tr><tr><td class="mono">panto --resume [<id>]</td><td>Resume the most recent session, or one by id prefix.</td></tr><tr><td class="mono">panto sessions</td><td>List saved sessions for this directory.</td></tr><tr><td class="mono">panto models sync</td><td>Fetch models.dev and rebuild the base models.toml.</td></tr><tr><td class="mono">panto auth …</td><td>Inspect and manage auth sessions (status / login / logout).</td></tr><tr><td class="mono">panto bootstrap [--force]</td><td>Run the luarocks bootstrap and exit.</td></tr><tr><td class="mono">panto lua [args…]</td><td>Drop into the embedded Lua interpreter.</td></tr><tr><td class="mono">panto help</td><td>Print the usage summary (also --help, -h).</td></tr></tbody></table></div><h2 id="starting" class="pd-h2">Starting a session</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto</span><span class="pd-cmd__note">Start a new conversation in the current directory.</span></div><div class="pd-cmd"><span class="pd-cmd__sig">panto --resume <span class="opt">[<id>]</span></span><span class="pd-cmd__note">Resume a previous session.</span></div><p class="pd-body">With no flag, <code>panto</code> opens a fresh session. <code>--resume</code> on its own reattaches the most recent session in this directory; <code>--resume <id></code> resumes the session whose id begins with <code><id></code>. Unknown arguments are tolerated (and ignored with a warning), so passing flags panto doesn’t recognise won’t stop it from starting.</p><div class="pg-callout pg-callout--warn"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Needs a terminal</p>The UI runs in raw mode and requires an interactive tty on stdin/stdout. Piping input or redirecting output will refuse with an error rather than half-start.</div></div><h2 id="sessions" class="pd-h2">panto sessions</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto sessions</span><span class="pd-cmd__note">List saved sessions for the current directory.</span></div><p class="pd-body">Sessions are grouped per working directory. Each line is the short id (the first eight hex characters of the session’s UUIDv7), the creation time trimmed to the minute, and the message count:</p><div class="pg-term"><div class="pg-term__bar"><span class="pg-term__dot" style="background:var(--red)"></span><span class="pg-term__dot" style="background:var(--brass)"></span><span class="pg-term__dot" style="background:var(--green)"></span><span class="pg-term__title">panto · sessions</span></div><div class="pg-term__body"><div class="pg-term__line pg-term__line--cmd"><span class="pg-term__prompt">$ </span>panto sessions</div><div class="pg-term__line pg-term__line--out">7f3a9c2b 2026-06-12 09:41 24 messages</div><div class="pg-term__line pg-term__line--out">b81e0d44 2026-06-11 17:03 6 messages</div><div class="pg-term__line pg-term__line--out">a0c7f190 2026-06-10 22:18 41 messages</div></div></div><p class="pd-body">When a directory has no sessions yet, panto prints <code>no sessions for <cwd></code>. Use any unambiguous id prefix from this list with <code>panto --resume</code>.</p><h2 id="models" class="pd-h2">panto models</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto models sync</span><span class="pd-cmd__note">Fetch models.dev and rebuild the base <code>models.toml</code>.</span></div><p class="pd-body"><code>sync</code> is the only action. It fetches the <a href="https://models.dev" target="_blank" rel="noreferrer">models.dev</a> catalog, walks the providers in your merged <a href="configuration.html#providers"><code>[providers]</code></a> config, and for each one cross-checks the catalog against the models that provider actually serves — then writes the result to the <strong>base-layer</strong> <code>models.toml</code> (<code>$PANTO_HOME/models.toml</code>, else <code>$XDG_DATA_HOME/panto/models.toml</code>). Aliases, wire names, context windows, token caps, and pricing come along for the ride. A provider maps onto a catalog entry via <a href="configuration.html#providers"><code>model_catalog_name</code></a> (falling back to the provider name); providers with no matching entry — or no usable synced models — are skipped.</p><div class="pg-term"><div class="pg-term__bar"><span class="pg-term__dot" style="background:var(--red)"></span><span class="pg-term__dot" style="background:var(--brass)"></span><span class="pg-term__dot" style="background:var(--green)"></span><span class="pg-term__title">panto · models sync</span></div><div class="pg-term__body"><div class="pg-term__line pg-term__line--cmd"><span class="pg-term__prompt">$ </span>panto models sync</div><div class="pg-term__line pg-term__line--out">synced 42 model(s) across 3 provider(s) to ~/.local/share/panto/models.toml</div></div></div><p class="pd-body">Because it writes the base layer, your own user/project <code>models.toml</code> entries always layer on top and win. Re-run it whenever you want to refresh the catalog after providers ship new models.</p><h2 id="auth" class="pd-h2">panto auth</h2><p class="pd-body">Manage the credentials behind your providers. With no action, <code>panto auth</code> defaults to <code>status</code>.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto auth status</span></div><div class="pd-opt__desc">List every configured auth session and its state. <code>api_key</code> sessions read <em>resolved</em> or <em>unresolved (key/env missing)</em>; <code>oauth_device</code> sessions show whether you’re logged in and roughly when the access token expires.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto auth login <name></span></div><div class="pd-opt__desc">Run the OAuth device flow for the named session. panto prints a verification URL and a user code, then stores (and, if configured, exchanges) the token. An <code>api_key</code> session has nothing to log in to.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto auth logout <name></span></div><div class="pd-opt__desc">Forget the stored token set for the named session.</div></div></div><div class="pg-term"><div class="pg-term__bar"><span class="pg-term__dot" style="background:var(--red)"></span><span class="pg-term__dot" style="background:var(--brass)"></span><span class="pg-term__dot" style="background:var(--green)"></span><span class="pg-term__title">panto · auth status</span></div><div class="pg-term__body"><div class="pg-term__line pg-term__line--cmd"><span class="pg-term__prompt">$ </span>panto auth status</div><div class="pg-term__line pg-term__line--ok">anthropic_api api_key resolved</div><div class="pg-term__line pg-term__line--err">openai_api api_key unresolved (key/env missing)</div><div class="pg-term__line pg-term__line--out">github_copilot oauth_device logged in (access expires in ~58m)</div></div></div><p class="pd-body">Auth sessions themselves are declared in config under <a href="configuration.html#auth"><code>[auth.<name>]</code></a>. The provider that uses a session names it with <code>auth = "<name>"</code>.</p><h2 id="bootstrap" class="pd-h2">panto bootstrap</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto bootstrap <span class="opt">[--force]</span></span><span class="pd-cmd__note">Run the luarocks bootstrap, then exit.</span></div><p class="pd-body">The bootstrap pipeline prepares the embedded Lua runtime: it stages headers and the base agent tree, materialises the default config, and installs panto’s pinned <a href="lua.html#runtime">batteries</a> under <code>$PANTO_HOME</code>. The agent runs the very same pipeline on startup, so you rarely need this by hand — it’s here for first-run setup on a clean machine and for scripted / CI installs. Repeat runs no-op quickly.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">--force</span></div><div class="pd-opt__desc">Wipe the per-Lua-version rocks tree (<code>$PANTO_HOME/rocks/lua-X.Y.Z/</code>) before reinstalling everything from scratch.</div></div></div><h2 id="lua" class="pd-h2">panto lua</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto lua <span class="opt">[args…]</span></span><span class="pd-cmd__note">Drop into the embedded Lua interpreter.</span></div><p class="pd-body">This is panto’s bundled Lua standalone (the same <code>lua.c</code> the agent embeds), with the luarocks runtime already bootstrapped — so <code>require("luarocks.*")</code> and any rocks installed under <code>$PANTO_HOME</code> resolve exactly as they do inside a session. Arguments after <code>lua</code> are passed straight through to the interpreter. Use it to experiment, or to drive luarocks to install extra rocks for your extensions:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code># run a script with panto’s Lua + rocks tree -panto lua my-script.lua - -# install an extra rock into panto’s tree via the embedded luarocks -panto lua -e 'arg[0]="luarocks"; require("luarocks.cmd").run_command("install","lpeg")'</code></pre></div></div><h2 id="slash-commands" class="pd-h2">Slash commands</h2><p class="pd-body">Inside a session, any line that begins with <code>/</code> is a <strong>slash command</strong> rather than a prompt — it is handled locally and never sent to the model. The first word (after the slash) is the command name; the trimmed remainder is passed as its arguments.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">/compact</span><span class="pg-badge pg-badge--neutral">builtin</span></div><div class="pd-opt__desc">Compact the conversation now — summarise older turns to reclaim context while keeping a verbatim recent tail. (Compaction also runs automatically; see <a href="configuration.html#compaction"><code>[compaction]</code></a>.)</div></div></div><p class="pd-body"><code>/compact</code> is the only built-in. Every other slash command comes from a Lua extension via <code>panto.ext.register_command</code> — see <a href="lua.html#commands">Lua extensions → register_command</a>. An unrecognised command reports an error instead of reaching the model.</p><h2 id="environment" class="pd-h2">Environment</h2><p class="pd-body">panto reads a few environment variables — for credentials and for locating its files. Configuration values can also pull from the environment via <code>${env:VAR}</code> substitution (see <a href="configuration.html#auth">Configuration</a>).</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Variable</th><th>Effect</th></tr></thead><tbody><tr><td class="mono">ANTHROPIC_API_KEY</td><td>Consumed by the default Anthropic provider.</td></tr><tr><td class="mono">OPENAI_API_KEY</td><td>Consumed by the default OpenAI provider.</td></tr><tr><td class="mono">PANTO_HOME</td><td>Override the runtime / rocks-tree location. Also where the base config.toml and agent tree are staged.</td></tr><tr><td class="mono">PANTO_SESSION_DIR</td><td>Override the base sessions directory. Defaults to $XDG_DATA_HOME/panto/sessions (or ~/.local/share/panto/sessions).</td></tr><tr><td class="mono">XDG_CONFIG_HOME</td><td>Locates the user config: $XDG_CONFIG_HOME/panto/ (else ~/.config/panto/).</td></tr><tr><td class="mono">XDG_DATA_HOME</td><td>Locates the base config + data: $XDG_DATA_HOME/panto/ (else ~/.local/share/panto/).</td></tr></tbody></table></div><nav class="pd-prevnext"><a class="pd-pn pd-pn--prev" href="index.html"><span class="pd-pn__dir">← Previous</span><span class="pd-pn__title">Getting started</span></a><a class="pd-pn pd-pn--next" href="configuration.html"><span class="pd-pn__dir">Next →</span><span class="pd-pn__title">Configuration</span></a></nav></article></main> +<main class="pd-main"><article class="pd-doc" data-screen-label="Command line"><p class="panto-eyebrow pd-eyebrow">Reference</p><h1 class="pd-doc__h1">Command line</h1><p class="pd-lead"><code>panto</code> dispatches on its first argument. Some names are true subcommands (<code>sessions</code>, <code>models</code>, <code>auth</code>, <code>bootstrap</code>, <code>update</code>, <code>lua</code>, <code>help</code>); the flag forms start the agent itself, either interactively or in one-shot print mode.</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Command</th><th>Does</th></tr></thead><tbody><tr><td class="mono">panto</td><td>Start a new interactive conversation in the current directory.</td></tr><tr><td class="mono">panto --resume [<id>]</td><td>Resume the most recent session, or a specific session by id prefix.</td></tr><tr><td class="mono">panto -c</td><td>Alias for bare <code>--resume</code>.</td></tr><tr><td class="mono">panto -p [prompt]</td><td>Run one non-interactive turn, print assistant text, exit 0/1.</td></tr><tr><td class="mono">panto sessions</td><td>List saved sessions for this directory.</td></tr><tr><td class="mono">panto models sync</td><td>Fetch models.dev and rebuild the base <code>models.toml</code>.</td></tr><tr><td class="mono">panto auth …</td><td>Show auth status, log in, or log out.</td></tr><tr><td class="mono">panto bootstrap [--force]</td><td>Prepare the bundled Lua / luarocks runtime and exit.</td></tr><tr><td class="mono">panto update</td><td>Install or refresh every rock listed in <code>extensions.rocks</code>.</td></tr><tr><td class="mono">panto lua [args…]</td><td>Run the embedded Lua interpreter.</td></tr><tr><td class="mono">panto --version</td><td>Print the panto version.</td></tr></tbody></table></div><h2 id="starting" class="pd-h2">Starting a session</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto</span><span class="pd-cmd__note">Start a fresh interactive conversation.</span></div><div class="pd-cmd"><span class="pd-cmd__sig">panto --resume <span class="opt">[<id>]</span></span><span class="pd-cmd__note">Resume the latest or a specific saved session.</span></div><div class="pd-cmd"><span class="pd-cmd__sig">panto -p, --print <span class="opt">[<prompt>]</span></span><span class="pd-cmd__note">Run one turn without the TUI.</span></div><p class="pd-body">Interactive mode requires a tty on stdin and stdout. Print mode does not: the prompt can come from the flag argument or from piped stdin, and the command prints only the assistant text before exiting.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">-c, --continue</span></div><div class="pd-opt__desc">Alias for bare <code>--resume</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">-m, --model <provider:alias></span></div><div class="pd-opt__desc">Override the model for this run. A bare alias also works when it is unique across providers.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">--effort <level></span></div><div class="pd-opt__desc">Override the reasoning level for this run. This beats both <code>models.toml</code> knobs and <code>[defaults] reasoning</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">--no-extensions</span></div><div class="pd-opt__desc">Skip the Lua runtime entirely. The core still starts, but because the shipped tools are extensions the agent runs with no tools.</div></div></div><div class="pg-callout pg-callout--warn"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Unknown arguments are errors now</p>Agent-mode flag parsing is strict. An unrecognized argument prints an error and exits instead of being ignored.</div></div><h2 id="sessions" class="pd-h2">panto sessions</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto sessions</span><span class="pd-cmd__note">List sessions for the current directory.</span></div><p class="pd-body">Sessions are stored per working directory. The listing is newest-modified first and prints the shortest unambiguous id prefix, the modified timestamp, message count, model, and last user message:</p><div class="pg-term"><div class="pg-term__bar"><span class="pg-term__dot" style="background:var(--red)"></span><span class="pg-term__dot" style="background:var(--brass)"></span><span class="pg-term__dot" style="background:var(--green)"></span><span class="pg-term__title">panto · sessions</span></div><div class="pg-term__body"><div class="pg-term__line pg-term__line--cmd"><span class="pg-term__prompt">$ </span>panto sessions</div><div class="pg-term__line pg-term__line--out">ID MODIFIED MSGS MODEL LAST MESSAGE</div><div class="pg-term__line pg-term__line--out">0197c2a4 2026-07-01 14:32 18 sonnet add json mode to stats</div><div class="pg-term__line pg-term__line--out">0197bfe1 2026-06-30 09:11 6 gpt-5 clean up the parser errors</div><div class="pg-term__line pg-term__line--out">~/.local/share/panto/sessions/--Users-travis-Code-ledger--</div></div></div><p class="pd-body">If there are no sessions yet, panto prints <code>no sessions for <cwd></code> and then the directory it would use.</p><h2 id="models" class="pd-h2">panto models</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto models sync</span><span class="pd-cmd__note">Fetch models.dev and rebuild the base <code>models.toml</code>.</span></div><p class="pd-body">The sync command looks at the providers that resolved from your layered config, maps each one to a models.dev catalog entry, optionally intersects that with the provider's own live <code>/models</code> listing, and writes the result to the base-layer <code>models.toml</code> at <code>$XDG_DATA_HOME/panto/models.toml</code> (or <code>~/.local/share/panto/models.toml</code>).</p><div class="pg-term"><div class="pg-term__bar"><span class="pg-term__dot" style="background:var(--red)"></span><span class="pg-term__dot" style="background:var(--brass)"></span><span class="pg-term__dot" style="background:var(--green)"></span><span class="pg-term__title">panto · models sync</span></div><div class="pg-term__body"><div class="pg-term__line pg-term__line--cmd"><span class="pg-term__prompt">$ </span>panto models sync</div><div class="pg-term__line pg-term__line--out">synced 42 model(s) across 3 provider(s) to ~/.local/share/panto/models.toml</div></div></div><p class="pd-body">Your user and project <code>models.toml</code> layers still sit on top and win.</p><h2 id="auth" class="pd-h2">panto auth</h2><p class="pd-body">With no action, <code>panto auth</code> defaults to <code>status</code>.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto auth status</span></div><div class="pd-opt__desc">Show every configured auth session. API-key sessions report <em>resolved</em> or <em>unresolved (key/env missing)</em>; OAuth sessions report whether they are logged in and when the current access token expires.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto auth login <name></span></div><div class="pd-opt__desc">Run the OAuth device flow for the named session and persist the resulting token set.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto auth logout <name></span></div><div class="pd-opt__desc">Delete the stored token set for the named session.</div></div></div><h2 id="bootstrap" class="pd-h2">panto bootstrap</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto bootstrap <span class="opt">[--force]</span></span><span class="pd-cmd__note">Run the runtime bootstrap pipeline, then exit.</span></div><p class="pd-body">Bootstrap stages the base agent tree, creates the generated base config as needed, and prepares the versioned luarocks tree under the data home. The normal agent startup path runs the same bootstrap automatically.</p><div class="pd-cmd"><span class="pd-cmd__sig">panto update</span><span class="pd-cmd__note">Install or refresh the rocks listed in <code>extensions.rocks</code>.</span></div><p class="pd-body"><code>panto update</code> is the explicit “go hit luarocks again” command. Startup only installs missing rocks; changing a version pin or forcing a re-resolve is what <code>update</code> is for.</p><h2 id="lua" class="pd-h2">panto lua</h2><div class="pd-cmd"><span class="pd-cmd__sig">panto lua <span class="opt">[args…]</span></span><span class="pd-cmd__note">Run the embedded Lua interpreter with panto's runtime wired in.</span></div><p class="pd-body">This is the same embedded Lua build panto uses internally, with package paths and luarocks configured the same way a session sees them. It is handy both for experiments and for driving luarocks manually:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>panto lua my-script.lua +panto lua -e 'arg[0]="luarocks"; require("luarocks.cmd").run_command("install","lpeg")'</code></pre></div></div><h2 id="slash-commands" class="pd-h2">Slash commands</h2><p class="pd-body">Lines beginning with <code>/</code> are handled locally inside the TUI and never sent to the model.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">Built in</span></div><div class="pd-opt__desc"><code>/help</code>, <code>/quit</code>, <code>/model</code>, <code>/reasoning</code>, <code>/new</code>, <code>/resume</code>, <code>/status</code>, and <code>/compact</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">Extension-defined</span></div><div class="pd-opt__desc">Lua extensions can register more slash commands with <code>panto.ext.register_command</code>.</div></div></div><h2 id="environment" class="pd-h2">Environment</h2><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Variable</th><th>Effect</th></tr></thead><tbody><tr><td class="mono">ANTHROPIC_API_KEY</td><td>Consumed by the default Anthropic provider.</td></tr><tr><td class="mono">OPENAI_API_KEY</td><td>Consumed by the default OpenAI provider.</td></tr><tr><td class="mono">PANTO_DEBUG</td><td>When non-zero, route <code>std.log</code> output to <code><data home>/debug/<session>.log</code> instead of the terminal.</td></tr><tr><td class="mono">PANTO_SESSION_DIR</td><td>Override the base sessions directory. panto still appends one encoded subdirectory per working directory.</td></tr><tr><td class="mono">XDG_CONFIG_HOME</td><td>Locates the user config and user extensions directories.</td></tr><tr><td class="mono">XDG_DATA_HOME</td><td>Locates the data home: base config, base models, sessions, auth cache, agent tree, and rocks.</td></tr></tbody></table></div><nav class="pd-prevnext"><a class="pd-pn pd-pn--prev" href="index.html"><span class="pd-pn__dir">← Previous</span><span class="pd-pn__title">Getting started</span></a><a class="pd-pn pd-pn--next" href="configuration.html"><span class="pd-pn__dir">Next →</span><span class="pd-pn__title">Configuration</span></a></nav></article></main> <aside class="pd-toc"><p class="pd-toc__title">On this page</p><ul class="pd-toc__list"><li><a href="#starting" class="pd-toc__link pd-toc__link--active">Starting a session</a></li><li><a href="#sessions" class="pd-toc__link">panto sessions</a></li><li><a href="#models" class="pd-toc__link">panto models</a></li><li><a href="#auth" class="pd-toc__link">panto auth</a></li><li><a href="#bootstrap" class="pd-toc__link">panto bootstrap</a></li><li><a href="#lua" class="pd-toc__link">panto lua</a></li><li><a href="#slash-commands" class="pd-toc__link">Slash commands</a></li><li><a href="#environment" class="pd-toc__link">Environment</a></li></ul></aside> </div> </div> diff --git a/configuration.html b/configuration.html index 1823478..48f5ee5 100644 --- a/configuration.html +++ b/configuration.html @@ -20,59 +20,34 @@ <div class="pd-header__right"><a class="pd-ghlink" href="https://code.tjp.lol/pantograph.git/" target="_blank" rel="noreferrer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 .5 5 .5 5 .5c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 7.5c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg> Source</a></div> </header> <div class="pd-shell"> -<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link pd-navsec__link--active">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Tools & extensions</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> +<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link pd-navsec__link--active">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Extensions policy</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> <div class="pd-scrim"></div> -<main class="pd-main"><article class="pd-doc" data-screen-label="Configuration"><p class="panto-eyebrow pd-eyebrow">Reference</p><h1 class="pd-doc__h1">Configuration</h1><div class="pd-doc__meta"><span class="pg-badge pg-badge--neutral">TOML</span></div><p class="pd-lead">panto is configured by layered <code>config.toml</code> files, plus a separate <code>models.toml</code> for model aliases and pricing. Define providers, pick a default model, and gate tools and extensions — everything you specify, nothing you don’t.</p><h2 id="files" class="pd-h2">Files & layers</h2><p class="pd-body">Four files are read and merged, lowest precedence first. Missing files are skipped silently.</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Layer</th><th>Path</th><th>Role</th></tr></thead><tbody><tr><td class="mono">base</td><td class="mono">$PANTO_HOME/config.toml · else $XDG_DATA_HOME/panto/config.toml</td><td>Auto-generated by bootstrap. Lowest precedence.</td></tr><tr><td class="mono">user</td><td class="mono">$XDG_CONFIG_HOME/panto/config.toml · else ~/.config/panto/config.toml</td><td>Your personal, machine-wide settings.</td></tr><tr><td class="mono">project</td><td class="mono">./.panto/config.toml</td><td>Checked-in, per-project settings.</td></tr><tr><td class="mono">local</td><td class="mono">./.panto/config.local.toml</td><td>Personal per-project overrides — git-ignore this. Highest precedence.</td></tr></tbody></table></div><p class="pd-body"><strong>Merge semantics:</strong> tables merge recursively, but scalars <em>and arrays</em> from a higher layer overwrite wholesale. A project file that sets <code>tools.deny</code> replaces the array entirely — it doesn’t append to a user-level list. Tables accumulate, so a provider defined only at the base layer survives even when a higher layer adds another.</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">config.toml</span><span class="pg-code__lang">toml</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code># ~/.config/panto/config.toml (user layer) +<main class="pd-main"><article class="pd-doc" data-screen-label="Configuration"><p class="panto-eyebrow pd-eyebrow">Reference</p><h1 class="pd-doc__h1">Configuration</h1><div class="pd-doc__meta"><span class="pg-badge pg-badge--neutral">TOML</span></div><p class="pd-lead">panto reads layered <code>config.toml</code> files for providers, auth, defaults, TUI settings, and extension policy. Model aliases and pricing live beside them in a separate layered <code>models.toml</code>.</p><h2 id="files" class="pd-h2">Files & layers</h2><p class="pd-body">Four <code>config.toml</code> layers are merged, lowest precedence first. Missing files are skipped.</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Layer</th><th>Path</th><th>Role</th></tr></thead><tbody><tr><td class="mono">base</td><td class="mono">$XDG_DATA_HOME/panto/config.toml · else ~/.local/share/panto/config.toml</td><td>Auto-generated defaults.</td></tr><tr><td class="mono">user</td><td class="mono">$XDG_CONFIG_HOME/panto/config.toml · else ~/.config/panto/config.toml</td><td>Machine-wide personal settings.</td></tr><tr><td class="mono">project</td><td class="mono">./.panto/config.toml</td><td>Checked-in project defaults.</td></tr><tr><td class="mono">local</td><td class="mono">./.panto/config.local.toml</td><td>Personal per-project overrides.</td></tr></tbody></table></div><p class="pd-body">Merge rule of thumb: tables merge recursively; scalars and arrays from a higher layer replace lower-layer values. One important exception is <code>[extensions]</code>: its allow/deny rules and source lists accumulate across layers so policy and extra extension sources can be refined instead of erased.</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">config.toml</span><span class="pg-code__lang">toml</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>[defaults] +model = "anthropic:sonnet" +reasoning = "high" -[defaults] -model = "anthropic:sonnet" # <provider>:<alias> +[tui] +editor = "code -w" +tools_collapsed = false [providers.anthropic] style = "anthropic_messages" base_url = "https://api.anthropic.com" -auth = "anthropic_api" # draws its key from [auth.anthropic_api] +auth = "anthropic_api" [auth.anthropic_api] -key = "${env:ANTHROPIC_API_KEY}" # resolved at load - -[tools] -deny = ["std.bash"] # allow every tool except the shell</code></pre></div></div><div class="pg-callout pg-callout--tip"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">The local layer</p><code>./.panto/config.local.toml</code> is meant to be git-ignored: apply your own overrides on top of the checked-in <code>project</code> layer without committing them.</div></div><h2 id="providers" class="pd-h2">[providers.<name>]</h2><p class="pd-body">A provider is pure transport: an API shape and a base URL, plus the name of the auth session that supplies its credential. The chosen name is what you reference on the left of a <code>provider:alias</code> model.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">style</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">The wire protocol: <code>anthropic_messages</code>, <code>openai_chat</code>, or <code>openai_responses</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">dialect</span><span class="pd-opt__type">"codex"</span></div><div class="pd-opt__desc">A sub-dialect for styles that share a protocol family. Only <code>style = "openai_responses"</code> accepts it today, with the single value <code>"codex"</code> (the Codex Responses variant).</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">base_url</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">The API base URL requests are sent to.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">auth</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Names the <a href="#auth"><code>[auth.<name>]</code></a> session that supplies this provider’s credential. (Provider-level <code>api_key</code> is no longer accepted — auth lives in one place.)</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">prompt_cache</span><span class="pd-opt__type">bool</span><span class="pg-badge pg-badge--neutral">default true</span></div><div class="pd-opt__desc"><code>anthropic_messages</code> only. Place one advancing <code>cache_control</code> breakpoint on each request. Ignored for other styles.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">extra_headers</span><span class="pd-opt__type">table</span></div><div class="pd-opt__desc">A table of static string headers merged onto every model request (see below).</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">model_catalog_name</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Maps this provider onto a <a href="https://models.dev" target="_blank" rel="noreferrer">models.dev</a> catalog key (e.g. <code>"github-copilot"</code>) for <a href="cli.html#models"><code>panto models sync</code></a>. Defaults to the provider name.</div></div></div><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">config.toml</span><span class="pg-code__lang">toml</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>[providers.copilot] +key = "${env:ANTHROPIC_API_KEY}"</code></pre></div></div><h2 id="providers" class="pd-h2">[providers.<name>]</h2><p class="pd-body">A provider describes transport only: API shape, base URL, auth session, and a few request-level knobs. The provider name is the left half of a <code>provider:alias</code> model reference.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">style</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">One of <code>anthropic_messages</code>, <code>openai_chat</code>, or <code>openai_responses</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">dialect</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">For <code>openai_responses</code>, the only current dialect is <code>"codex"</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">base_url</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">API base URL.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">auth</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Names the <a href="#auth"><code>[auth.<name>]</code></a> session used for this provider.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">prompt_cache</span><span class="pd-opt__type">bool</span></div><div class="pd-opt__desc">Anthropic only. Controls the advancing <code>cache_control</code> breakpoint.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">extra_headers</span><span class="pd-opt__type">table</span></div><div class="pd-opt__desc">Static request headers merged onto every request.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">model_catalog_name</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Optional models.dev provider key for <code>panto models sync</code>.</div></div></div><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">config.toml</span><span class="pg-code__lang">toml</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>[providers.copilot] style = "openai_chat" base_url = "https://api.individual.githubcopilot.com" auth = "github_copilot" [providers.copilot.extra_headers] Copilot-Integration-Id = "vscode-chat" -X-Initiator = "user"</code></pre></div></div><div class="pg-callout pg-callout--note"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Unresolved providers vanish</p>If a provider’s <code>api_key</code> auth session resolves to empty (its env var isn’t set), the provider is dropped — export the key to bring it back. OAuth providers always survive and resolve at turn time.</div></div><h2 id="auth" class="pd-h2">[auth.<name>]</h2><p class="pd-body">A named auth session supplies a credential. Its <code>type</code> is inferred when omitted: a <code>key</code> implies <code>api_key</code>; a <code>client_id</code> implies <code>oauth_device</code>.</p><h3 id="auth-subst" class="pd-h3">Substitution</h3><p class="pd-body">Every auth value supports <code>${…}</code> substitution. <code>${env:VAR}</code> reads the environment; <code>${name}</code> reads another key in the same section (handy for a shared <code>domain</code>). An unresolved reference becomes the empty string.</p><h3 id="auth-apikey" class="pd-h3">API-key sessions</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">key</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">The API key — usually <code>"${env:VAR}"</code>, but a literal works too. Resolved eagerly at load; an empty result leaves the session unresolved.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">type</span><span class="pd-opt__type">"api_key"</span></div><div class="pd-opt__desc">Optional; inferred from the presence of <code>key</code>.</div></div></div><h3 id="auth-oauth" class="pd-h3">OAuth device sessions</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">client_id</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">The OAuth client id.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">device_code_url</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Endpoint that issues the device + user codes.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">token_url</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Endpoint polled for the access token.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">dialect</span><span class="pd-opt__type">"token" | "codex"</span><span class="pg-badge pg-badge--neutral">default token</span></div><div class="pd-opt__desc">Flow dialect. <code>codex</code> additionally requires <code>device_poll_url</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">scope</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Requested OAuth scopes.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">verification_url</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Override the URL shown to the user.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">device_poll_url</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Distinct poll endpoint (required for <code>codex</code>).</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">token_request_format</span><span class="pd-opt__type">"form" | "json"</span><span class="pg-badge pg-badge--neutral">default form</span></div><div class="pd-opt__desc">How the token request body is encoded.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">redirect_uri / account_id_jwt_claim</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Optional flow details.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">exchange_url</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Enables an optional secondary token exchange. Companions: <code>exchange_method</code> (default <code>GET</code>), <code>exchange_token_path</code> (default <code>token</code>), <code>exchange_expires_path</code>, <code>exchange_base_url_path</code> — JSON paths into the exchange response.</div></div></div><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">config.toml</span><span class="pg-code__lang">toml</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>[auth.github_copilot] -client_id = "Iv1.b507a08c87ecfe98" -domain = "github.com" # an ordinary sibling key… -device_code_url = "https://${domain}/login/device/code" # …reused here -token_url = "https://${domain}/login/oauth/access_token" -scope = "read:user" - -# optional secondary token exchange -exchange_url = "https://api.${domain}/copilot_internal/v2/token" -exchange_expires_path = "expires_at" -exchange_base_url_path = "endpoints.api"</code></pre></div></div><h2 id="defaults" class="pd-h2">[defaults]</h2><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">model</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">The default model, as <code>"<provider>:<alias>"</code> (exactly one colon, both halves non-empty). The provider must resolve.</div></div></div><p class="pd-body">Model selection falls through in order: an explicit override (a future <code>--model</code>), then <code>defaults.model</code>, then — if exactly one provider resolved and it has exactly one alias in <code>models.toml</code> — that one. Otherwise panto asks you to set a default.</p><h2 id="policies" class="pd-h2">Tools & extensions</h2><p class="pd-body"><code>[tools]</code> and <code>[extensions]</code> share the same shape: allow- and deny-lists of glob patterns over dotted names (the standard tools live under the <code>std.</code> namespace — <code>std.read</code>, <code>std.write</code>, <code>std.edit</code>, <code>std.bash</code>).</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">allow</span><span class="pd-opt__type">string[]</span></div><div class="pd-opt__desc">Glob patterns. <strong>Empty means allow-all</strong> (still subject to <code>deny</code>).</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">deny</span><span class="pd-opt__type">string[]</span></div><div class="pd-opt__desc">Glob patterns. A name is denied if it matches any deny pattern.</div></div></div><p class="pd-body">A name is permitted when it matches at least one <code>allow</code> pattern (or allow is empty) <em>and</em> matches no <code>deny</code> pattern.</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">config.toml</span><span class="pg-code__lang">toml</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>[tools] -allow = ["std.*"] # only the standard tools -deny = ["std.bash"] # …but never the shell</code></pre></div></div><div class="pg-callout pg-callout--danger"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Conflicts are fatal</p>The same pattern in both <code>allow</code> and <code>deny</code> for one kind is a hard error — panto refuses to start rather than guess.</div></div><h2 id="compaction" class="pd-h2">[compaction]</h2><p class="pd-body">panto compacts long conversations automatically; this section tunes it. The manual <a href="cli.html#slash-commands"><code>/compact</code></a> command uses the same settings.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">keep_verbatim</span><span class="pd-opt__type">int</span><span class="pg-badge pg-badge--neutral">default 20000</span></div><div class="pd-opt__desc">Token budget for the recent tail kept verbatim when compacting.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">model</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Optional <code>provider:alias</code> override for the model that writes the summary. Defaults to the active chat model.</div></div></div><h2 id="models" class="pd-h2">models.toml</h2><p class="pd-body">Model <em>aliases</em> live in <code>$XDG_CONFIG_HOME/panto/models.toml</code>, separate from <code>config.toml</code>. Each entry is keyed <code>[<provider>.<alias>]</code> — the provider matches a <code>[providers.<name>]</code>, the alias is the short name you reference. A referenced alias with no entry isn’t an error: it’s used verbatim as the wire name with default knobs.</p><p class="pd-body"><code>models.toml</code> is layered exactly like <code>config.toml</code> (base → user → project → local). The base layer is auto-generated — <a href="cli.html#models"><code>panto models sync</code></a> fetches the catalog and writes it — so your user and project entries always layer on top and win.</p><h3 id="models-common" class="pd-h3">Common keys</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">model</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Wire model id sent to the API. Defaults to the alias.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">context_window</span><span class="pd-opt__type">int</span></div><div class="pd-opt__desc">Total input context window, used for the footer’s context-usage display. Optional.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">max_tokens</span><span class="pd-opt__type">int</span></div><div class="pd-opt__desc">Per-request output token cap.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">input / output / cache_read / cache_write</span><span class="pd-opt__type">float</span></div><div class="pd-opt__desc">Pricing in USD per million tokens. All optional; omitted means unknown.</div></div></div><h3 id="models-openai" class="pd-h3">openai_chat only</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">reasoning</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--neutral">default default</span></div><div class="pd-opt__desc">One of <code>default</code>, <code>off</code>, <code>minimal</code>, <code>low</code>, <code>medium</code>, <code>high</code>.</div></div></div><h3 id="models-anthropic" class="pd-h3">anthropic_messages only</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">thinking</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--neutral">default disabled</span></div><div class="pd-opt__desc"><code>disabled</code>, <code>enabled</code>, or <code>adaptive</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">effort</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--neutral">default medium</span></div><div class="pd-opt__desc"><code>low</code> · <code>medium</code> · <code>high</code> · <code>xhigh</code> · <code>max</code>. Used when <code>thinking = "adaptive"</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">thinking_budget_tokens</span><span class="pd-opt__type">int</span><span class="pg-badge pg-badge--neutral">default 32000</span></div><div class="pd-opt__desc">Reasoning-token budget when <code>thinking = "enabled"</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">thinking_interleaved</span><span class="pd-opt__type">bool</span><span class="pg-badge pg-badge--neutral">default false</span></div><div class="pd-opt__desc">Send the interleaved-thinking beta header (honoured when <code>thinking = "enabled"</code>).</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">api_version</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--neutral">default 2023-06-01</span></div><div class="pd-opt__desc">The <code>anthropic-version</code> header.</div></div></div><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">models.toml</span><span class="pg-code__lang">toml</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code># ~/.config/panto/models.toml - -[anthropic.sonnet] -model = "claude-sonnet-4-20250514" # wire name; defaults to the alias -max_tokens = 8192 -thinking = "enabled" -thinking_budget_tokens = 16000 -input = 3.0 # USD per million tokens -output = 15.0 -cache_read = 0.30 -cache_write = 3.75 - -[openai.gpt] -model = "gpt-4o" -reasoning = "medium" -input = 2.5 -output = 10.0</code></pre></div></div><h2 id="system-prompt" class="pd-h2">System prompt</h2><p class="pd-body">The agent’s system prompt is sourced by convention from Markdown files across the same base → user → project layers (base is <code>$PANTO_HOME/agent</code>, where bootstrap stages the bundled default):</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">SYSTEM.md</span></div><div class="pd-opt__desc">Replaces the system prompt for that layer.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">APPEND_SYSTEM.md</span></div><div class="pd-opt__desc">Appends to it — for small, additive house rules.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">COMPACTION.md</span></div><div class="pd-opt__desc">Overrides the summary prompt used during <a href="#compaction">compaction</a> (last layer wins; a built-in default applies otherwise).</div></div></div><p class="pd-body">On resume, prompt changes are reconciled without rewriting the existing conversation.</p><nav class="pd-prevnext"><a class="pd-pn pd-pn--prev" href="cli.html"><span class="pd-pn__dir">← Previous</span><span class="pd-pn__title">Command line</span></a><a class="pd-pn pd-pn--next" href="keybindings.html"><span class="pd-pn__dir">Next →</span><span class="pd-pn__title">Key bindings</span></a></nav></article></main> -<aside class="pd-toc"><p class="pd-toc__title">On this page</p><ul class="pd-toc__list"><li><a href="#files" class="pd-toc__link pd-toc__link--active">Files & layers</a></li><li><a href="#providers" class="pd-toc__link">[providers]</a></li><li><a href="#auth" class="pd-toc__link">[auth]</a></li><li><a href="#defaults" class="pd-toc__link">[defaults]</a></li><li><a href="#policies" class="pd-toc__link">Tools & extensions</a></li><li><a href="#compaction" class="pd-toc__link">[compaction]</a></li><li><a href="#models" class="pd-toc__link">models.toml</a></li><li><a href="#system-prompt" class="pd-toc__link">System prompt</a></li></ul></aside> +X-Initiator = "user"</code></pre></div></div><h2 id="auth" class="pd-h2">[auth.<name>]</h2><p class="pd-body">Auth sessions are named, reusable credential sources. If <code>type</code> is omitted, panto infers it from the fields present: <code>key</code> implies <code>api_key</code>; <code>client_id</code> implies <code>oauth_device</code>.</p><h3 id="auth-subst" class="pd-h3">Substitution</h3><p class="pd-body">Every auth value supports <code>${...}</code> substitution. <code>${env:VAR}</code> reads the environment; <code>${name}</code> reads another key in the same auth block. Unresolved substitutions become the empty string.</p><h3 id="auth-apikey" class="pd-h3">API-key sessions</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">key</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Usually <code>"${env:VAR}"</code>. Resolved eagerly at load time.</div></div></div><h3 id="auth-oauth" class="pd-h3">OAuth device sessions</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">client_id / device_code_url / token_url</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Core device-flow endpoints.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">dialect</span><span class="pd-opt__type">token | codex</span></div><div class="pd-opt__desc"><code>codex</code> additionally requires <code>device_poll_url</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">scope / verification_url / redirect_uri</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Optional flow details.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">exchange_…</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Optional secondary token exchange after device login.</div></div></div><h2 id="defaults" class="pd-h2">[defaults]</h2><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">model</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Default model as <code>"<provider>:<alias>"</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">reasoning</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Session-default reasoning label — the same names the <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">R</span> picker shows.</div></div></div><p class="pd-body">Model selection falls through in this order: <code>--model</code>, then <code>[defaults] model</code>, then the single-provider/single-alias convenience case. Reasoning falls through similarly: <code>--effort</code> beats a per-alias setting in <code>models.toml</code>, which beats <code>[defaults] reasoning</code>.</p><h3 class="pd-h3">[tui]</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">editor</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Overrides <code>$VISUAL</code> / <code>$EDITOR</code> for <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">G</span>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">tools_collapsed</span><span class="pd-opt__type">bool</span></div><div class="pd-opt__desc">Initial state of the global tool-output collapse toggle.</div></div></div><h2 id="policies" class="pd-h2">Extensions policy & sources</h2><p class="pd-body"><code>[extensions]</code> governs every Lua-authored capability, including the shipped standard tools. The names you match are extension entry names such as <code>std.read</code>, <code>std.write</code>, <code>std.edit</code>, and <code>std.shell</code>.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">allow / deny</span><span class="pd-opt__type">string[]</span></div><div class="pd-opt__desc">Glob rules over entry names. The default is allow; later, more specific rules win, and <code>deny</code> wins an exact tie.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">paths</span><span class="pd-opt__type">string[]</span></div><div class="pd-opt__desc">Extra directories to scan for extension sources.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">rocks</span><span class="pd-opt__type">string[]</span></div><div class="pd-opt__desc">Installed rock specs to load as extension sources. <code>panto update</code> installs or refreshes them.</div></div></div><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">config.toml</span><span class="pg-code__lang">toml</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>[extensions] +deny = ["std.shell"] +paths = ["./dev/panto-ext"] +rocks = ["panto-agent 1.4.2-1"]</code></pre></div></div><h2 id="compaction" class="pd-h2">[compaction]</h2><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">keep_verbatim</span><span class="pd-opt__type">int</span></div><div class="pd-opt__desc">Token budget for the recent tail kept verbatim during compaction.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">model</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Optional <code>provider:alias</code> override for the compaction model.</div></div></div><h2 id="models" class="pd-h2">models.toml</h2><p class="pd-body"><code>models.toml</code> is layered just like <code>config.toml</code>: base at <code>$XDG_DATA_HOME/panto/models.toml</code>, then user, project, and <code>./.panto/models.local.toml</code>. Each entry is keyed <code>[<provider>.<alias>]</code>. If an alias is missing entirely, panto simply uses the alias itself as the wire model name.</p><h3 id="models-common" class="pd-h3">Common keys</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">model</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Wire model id sent to the provider API. Defaults to the alias.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">context_window / max_tokens</span><span class="pd-opt__type">int</span></div><div class="pd-opt__desc">Context metadata for the UI and a per-request output cap.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">input / output / cache_read / cache_write</span><span class="pd-opt__type">float</span></div><div class="pd-opt__desc">Pricing in USD per million tokens.</div></div></div><h3 id="models-openai" class="pd-h3">OpenAI-style knobs</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">reasoning</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">Applies to <code>openai_chat</code> and <code>openai_responses</code>: <code>default</code>, <code>off</code>, <code>minimal</code>, <code>low</code>, <code>medium</code>, or <code>high</code>.</div></div></div><h3 id="models-anthropic" class="pd-h3">Anthropic-style knobs</h3><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">thinking</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc"><code>disabled</code>, <code>enabled</code>, or <code>adaptive</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">effort</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc"><code>low</code>, <code>medium</code>, <code>high</code>, <code>xhigh</code>, or <code>max</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">thinking_budget_tokens / thinking_interleaved / api_version</span><span class="pd-opt__type">int | bool | string</span></div><div class="pd-opt__desc">Manual-thinking budget, interleaved-thinking beta header, and Anthropic API version.</div></div></div><h2 id="system-prompt" class="pd-h2">System prompt</h2><p class="pd-body">Prompt files are layered beside the extension tree: base under <code>$XDG_DATA_HOME/panto/agent/</code>, then user, then project. The conventional files are <code>SYSTEM.md</code>, <code>APPEND_SYSTEM.md</code>, and <code>COMPACTION.md</code>.</p><nav class="pd-prevnext"><a class="pd-pn pd-pn--prev" href="cli.html"><span class="pd-pn__dir">← Previous</span><span class="pd-pn__title">Command line</span></a><a class="pd-pn pd-pn--next" href="keybindings.html"><span class="pd-pn__dir">Next →</span><span class="pd-pn__title">Key bindings</span></a></nav></article></main> +<aside class="pd-toc"><p class="pd-toc__title">On this page</p><ul class="pd-toc__list"><li><a href="#files" class="pd-toc__link pd-toc__link--active">Files & layers</a></li><li><a href="#providers" class="pd-toc__link">[providers]</a></li><li><a href="#auth" class="pd-toc__link">[auth]</a></li><li><a href="#defaults" class="pd-toc__link">[defaults]</a></li><li><a href="#policies" class="pd-toc__link">Extensions policy</a></li><li><a href="#compaction" class="pd-toc__link">[compaction]</a></li><li><a href="#models" class="pd-toc__link">models.toml</a></li><li><a href="#system-prompt" class="pd-toc__link">System prompt</a></li></ul></aside> </div> </div> <script src="_shared/docs.js"></script> @@ -20,17 +20,15 @@ <div class="pd-header__right"><a class="pd-ghlink" href="https://code.tjp.lol/pantograph.git/" target="_blank" rel="noreferrer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 .5 5 .5 5 .5c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 7.5c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg> Source</a></div> </header> <div class="pd-shell"> -<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link pd-navsec__link--active">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Tools & extensions</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> +<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link pd-navsec__link--active">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Extensions policy</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> <div class="pd-scrim"></div> -<main class="pd-main"><article class="pd-doc" data-screen-label="panto"><p class="panto-eyebrow pd-eyebrow">Getting started</p><h1 class="pd-doc__h1">panto</h1><div class="pd-doc__meta"><span class="pg-badge pg-badge--neutral">terminal agent</span><span class="pg-tag"><span class="pg-tag__dot" style="background:var(--red)"></span>zig</span><span class="pg-tag"><span class="pg-tag__dot" style="background:var(--green)"></span>lua</span></div><p class="pd-lead">panto is a small terminal coding agent built on libpanto. A minimal system prompt, a tool set you can swap or switch off, native binaries from Zig, extensions in Lua. This is the operator's reference — flags, configuration, key bindings, and the Lua surface.</p><h2 id="overview" class="pd-h2">Overview</h2><p class="pd-body">You run <code>panto</code> inside a project directory and talk to a model in a terminal UI. The agent reads and edits files and runs commands through <strong>tools</strong> — and even the standard tools (<code>read</code>, <code>write</code>, <code>edit</code>, <code>bash</code>) ship as extensions, so any of them can be denied individually.</p><p class="pd-body">There are no permission prompts and no hidden machinery: providers are declared in a TOML file, the model is chosen by a <code>provider:alias</code> reference, and anything beyond the core is added as a Lua extension. The rest of these pages document each of those surfaces in turn.</p><div class="panto-tick-rule pd-rule"></div><h2 id="install" class="pd-h2">Install</h2><p class="pd-body">panto builds from source with <a href="https://ziglang.org" target="_blank" rel="noreferrer">Zig</a>. Clone the repository and run the Zig build; the result is a single native binary.</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>git clone https://code.tjp.lol/pantograph.git +<main class="pd-main"><article class="pd-doc" data-screen-label="panto"><p class="panto-eyebrow pd-eyebrow">Getting started</p><h1 class="pd-doc__h1">panto</h1><div class="pd-doc__meta"><span class="pg-badge pg-badge--neutral">terminal agent</span><span class="pg-tag"><span class="pg-tag__dot" style="background:var(--red)"></span>zig</span><span class="pg-tag"><span class="pg-tag__dot" style="background:var(--green)"></span>lua</span></div><p class="pd-lead">panto is a terminal coding agent with a small Zig core and a Lua extension surface. Run it interactively in a project directory, or use <code>-p/--print</code> for one-shot non-interactive turns. These pages cover the current CLI, configuration layers, key bindings, and extension API.</p><h2 id="overview" class="pd-h2">Overview</h2><p class="pd-body">The default entry point is just <code>panto</code>: it opens the TUI in the current directory, selects a model from your layered config, and lets the agent work through tools. The shipped file tools are themselves Lua extensions, so the same loader that powers your own add-ons also powers <code>std.read</code>, <code>std.write</code>, <code>std.edit</code>, and <code>std.shell</code>.</p><p class="pd-body">The first argument also dispatches subcommands. <code>panto sessions</code>, <code>panto auth</code>, <code>panto models sync</code>, <code>panto bootstrap</code>, <code>panto update</code>, and <code>panto lua</code> all short-circuit before the chat loop; <code>panto -p</code> runs a single non-interactive turn and exits.</p><div class="panto-tick-rule pd-rule"></div><h2 id="install" class="pd-h2">Install</h2><p class="pd-body">panto builds from source with <a href="https://ziglang.org" target="_blank" rel="noreferrer">Zig</a>. Clone the repository and build the binary:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>git clone https://code.tjp.lol/pantograph.git cd pantograph -zig build # native binary at zig-out/bin/panto</code></pre></div></div><p class="pd-body">On first use, run the one-time bootstrap. It stages the embedded Lua runtime and installs panto’s pinned <a href="lua.html#runtime">luarocks</a> batteries (just <code>luv</code>, the libuv event loop) under <code>$PANTO_HOME</code>:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>panto bootstrap # prepare the rocks tree (idempotent)</code></pre></div></div><div class="pg-callout pg-callout--tip"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Where things live</p>By default panto keeps its runtime and rocks tree in <code>$PANTO_HOME</code> (falling back to <code>$XDG_DATA_HOME/panto</code>), and reads configuration from a layered set of TOML files. See <a href="configuration.html#files">Configuration → Files & layers</a>.</div></div><h2 id="first-run" class="pd-h2">First run</h2><p class="pd-body">Run <code>panto</code> with no arguments inside a project to start a fresh conversation. The agent loads your config, selects the default model, and opens the terminal UI. Type a message and press <span class="pd-kbd">Enter</span> to send it.</p><div class="pg-term"><div class="pg-term__bar"><span class="pg-term__dot" style="background:var(--red)"></span><span class="pg-term__dot" style="background:var(--brass)"></span><span class="pg-term__dot" style="background:var(--green)"></span><span class="pg-term__title">panto · first run</span></div><div class="pg-term__body"><div class="pg-term__line pg-term__line--cmd"><span class="pg-term__prompt">$ </span>export ANTHROPIC_API_KEY=sk-ant-…</div><div class="pg-term__line pg-term__line--cmd"><span class="pg-term__prompt">$ </span>cd ~/code/ledger && panto</div><div class="pg-term__line"> </div><div class="pg-term__line pg-term__line--accent"> panto v0.1.0</div><div class="pg-term__line pg-term__line--dim"> cwd: ~/code/ledger</div><div class="pg-term__line"> </div><div class="pg-term__user">add a --json flag to the stats command</div><div class="pg-term__line"> </div><div class="pg-term__line">I’ll wire a <code>--json</code> branch into the stats command. Let me read it first.</div><div class="pg-term__tool"><div class="pg-term__tool-head">read src/stats.zig</div><div class="pg-term__tool-body">pub fn run(args: Args) !void { - const summary = try compute(args); - try printHuman(summary); -}</div></div><div class="pg-term__tool"><div class="pg-term__tool-head">edit src/stats.zig (1 edit)</div><div class="pg-term__tool-body"><span class="pg-term__del">- try printHuman(summary);</span> -<span class="pg-term__add">+ if (args.json) try printJson(summary) else try printHuman(summary);</span></div></div><div class="pg-term__line"> </div><div class="pg-term__line">Done — <code>stats --json</code> now prints a machine-readable summary.</div><div class="pg-term__line"> </div><div class="pg-term__rule"></div><div class="pg-term__line pg-term__line--cmd"> <span class="pg-term__cursor"></span></div><div class="pg-term__rule"></div><div class="pg-term__line pg-term__line--dim">anthropic:sonnet 18.4k ctx 2.1k tok $0.03</div></div></div><p class="pd-body">panto’s UI needs an interactive terminal (a tty). Sessions are saved per directory, so you can pick up where you left off with <code>panto --resume</code>. The full command surface — resuming, listing sessions, auth, the embedded Lua interpreter — is on the <a href="cli.html#starting">Command line</a> page, and every key is listed under <a href="keybindings.html#editing">Key bindings</a>.</p><h2 id="authenticate" class="pd-h2">Authenticate</h2><p class="pd-body">A provider draws its credential from a named <em>auth session</em>. There are two kinds. The simplest is an API key from the environment — the default OpenAI and Anthropic providers read <code>OPENAI_API_KEY</code> and <code>ANTHROPIC_API_KEY</code> directly:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>export ANTHROPIC_API_KEY=sk-ant-… -panto # the anthropic provider is now live</code></pre></div></div><p class="pd-body">The second is an OAuth device flow, for providers that authenticate interactively. Log in once and panto stores (and refreshes) the token for you:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>panto auth login github_copilot -panto auth status # show every session and its login state</code></pre></div></div><div class="pg-callout pg-callout--note"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">If a provider seems missing</p>An <code>api_key</code> session whose environment variable is empty resolves to nothing, and any provider using it is silently dropped (export the key to bring it back). OAuth providers always survive and resolve at turn time. Auth sessions are declared under <a href="configuration.html#auth"><code>[auth]</code></a> in config.</div></div><nav class="pd-prevnext"><span style="flex:1"></span><a class="pd-pn pd-pn--next" href="cli.html"><span class="pd-pn__dir">Next →</span><span class="pd-pn__title">Command line</span></a></nav></article></main> +zig build # native binary at zig-out/bin/panto</code></pre></div></div><p class="pd-body">The first real run bootstraps panto's bundled Lua runtime automatically, so most users can go straight to <code>panto</code>. The explicit bootstrap command is still useful for CI, fresh-machine setup, or forcing the rocks tree to exist before you start a session:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>panto bootstrap +# or: panto bootstrap --force</code></pre></div></div><div class="pg-callout pg-callout--tip"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Where things live</p>The data home is <code>$XDG_DATA_HOME/panto</code> (or <code>~/.local/share/panto</code> when <code>XDG_DATA_HOME</code> is unset). User config lives under <code>$XDG_CONFIG_HOME/panto</code> (or <code>~/.config/panto</code>). Project-local overrides live in <code>./.panto/</code>.</div></div><h2 id="first-run" class="pd-h2">First run</h2><p class="pd-body">Start an interactive session with no arguments:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>cd ~/code/ledger +panto</code></pre></div></div><p class="pd-body">Useful first-day flags:</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto --resume</span></div><div class="pd-opt__desc">Reattach the most recent conversation in this directory. <code>--resume <id></code> resumes a specific session by id prefix; <code>-c</code> / <code>--continue</code> is an alias for bare <code>--resume</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto -p "prompt"</span></div><div class="pd-opt__desc">Run one turn non-interactively, print only the assistant text, and exit. The prompt may also come from piped stdin.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto -m openai:gpt --effort high</span></div><div class="pd-opt__desc">Override the model and reasoning level for just this run.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">panto --no-extensions</span></div><div class="pd-opt__desc">Skip the Lua runtime entirely. This disables shipped tools too, but it is a useful escape hatch when an extension breaks startup.</div></div></div><p class="pd-body">The TUI requires an interactive terminal. If you only want a single answer in a script or CI job, use <code>-p</code> instead of trying to pipe the full-screen UI.</p><h2 id="authenticate" class="pd-h2">Authenticate</h2><p class="pd-body">Providers draw credentials from named auth sessions in <code>config.toml</code>. API-key sessions usually resolve from the environment:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>export ANTHROPIC_API_KEY=sk-ant-… +panto auth status</code></pre></div></div><p class="pd-body">OAuth device sessions are logged in explicitly once, then cached on disk:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>panto auth login github_copilot +panto auth status</code></pre></div></div><div class="pg-callout pg-callout--note"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">If a provider seems missing</p>An <code>api_key</code> auth session that resolves to the empty string leaves its provider unavailable, so exporting the missing environment variable is often enough to make it appear. OAuth providers stay configured and resolve at use time.</div></div><nav class="pd-prevnext"><span style="flex:1"></span><a class="pd-pn pd-pn--next" href="cli.html"><span class="pd-pn__dir">Next →</span><span class="pd-pn__title">Command line</span></a></nav></article></main> <aside class="pd-toc"><p class="pd-toc__title">On this page</p><ul class="pd-toc__list"><li><a href="#overview" class="pd-toc__link pd-toc__link--active">Overview</a></li><li><a href="#install" class="pd-toc__link">Install</a></li><li><a href="#first-run" class="pd-toc__link">First run</a></li><li><a href="#authenticate" class="pd-toc__link">Authenticate</a></li></ul></aside> </div> </div> diff --git a/keybindings.html b/keybindings.html index 09739d8..747db1f 100644 --- a/keybindings.html +++ b/keybindings.html @@ -20,9 +20,9 @@ <div class="pd-header__right"><a class="pd-ghlink" href="https://code.tjp.lol/pantograph.git/" target="_blank" rel="noreferrer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 .5 5 .5 5 .5c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 7.5c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg> Source</a></div> </header> <div class="pd-shell"> -<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Tools & extensions</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link pd-navsec__link--active">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> +<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Extensions policy</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link pd-navsec__link--active">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> <div class="pd-scrim"></div> -<main class="pd-main"><article class="pd-doc" data-screen-label="Key bindings"><p class="panto-eyebrow pd-eyebrow">Reference</p><h1 class="pd-doc__h1">Key bindings</h1><div class="pd-doc__meta"><span class="pg-badge pg-badge--neutral">TUI input</span></div><p class="pd-lead">The session UI reads raw keystrokes and turns them into editing actions. These are the keys it recognises today; panto negotiates richer terminal protocols at startup so the important ones (notably Shift+Enter) work wherever the terminal allows.</p><h2 id="editing" class="pd-h2">Editing & cursor</h2><p class="pd-body">The input box is a multi-line editor. Printable keys insert text (full Unicode), and the usual motion keys move the cursor.</p><div class="pd-table-wrap"><table class="pd-table pd-keytable"><thead><tr><th style="width:34%">Key</th><th>Action</th></tr></thead><tbody><tr><td>any printable key</td><td>Insert the character (multi-byte UTF-8 supported).</td></tr><tr><td><span class="pd-kbd">←</span> / <span class="pd-kbd">→</span></td><td>Move the cursor one character.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">←</span> / <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">→</span><br><span class="pd-kbd">Alt</span>+<span class="pd-kbd">←</span> / <span class="pd-kbd">Alt</span>+<span class="pd-kbd">→</span> · <span class="pd-kbd">Alt</span>+<span class="pd-kbd">B</span> / <span class="pd-kbd">Alt</span>+<span class="pd-kbd">F</span></td><td>Move the cursor by word (terminals vary which form they send).</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">A</span> / <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">E</span></td><td>Jump to the start / end of the current line.</td></tr><tr><td><span class="pd-kbd">Home</span> / <span class="pd-kbd">End</span></td><td>Jump to the very start / end of the whole input.</td></tr><tr><td><span class="pd-kbd">Backspace</span></td><td>Delete the character before the cursor.</td></tr><tr><td><span class="pd-kbd">Delete</span></td><td>Delete the character under the cursor.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">W</span> · <span class="pd-kbd">Alt</span>+<span class="pd-kbd">Backspace</span> · <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">Backspace</span></td><td>Delete the previous word.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">U</span></td><td>Delete from the cursor back to the start of the line.</td></tr><tr><td>paste</td><td>Pasted text is inserted literally as one run (bracketed paste), not interpreted key-by-key.</td></tr></tbody></table></div><h2 id="submit" class="pd-h2">Submit & newline</h2><div class="pd-table-wrap"><table class="pd-table pd-keytable"><thead><tr><th style="width:34%">Key</th><th>Action</th></tr></thead><tbody><tr><td><span class="pd-kbd">Enter</span></td><td>Send the message to the model.</td></tr><tr><td><span class="pd-kbd">Shift</span>+<span class="pd-kbd">Enter</span></td><td>Insert a newline instead of sending (terminal permitting — see below).</td></tr></tbody></table></div><p class="pd-body">Whether <span class="pd-kbd">Shift</span>+<span class="pd-kbd">Enter</span> inserts a newline depends on your terminal, because in the bare legacy protocol it is indistinguishable from plain <span class="pd-kbd">Enter</span>. panto works around this where it can — see <a href="#terminal">Terminal protocol</a>.</p><h2 id="control" class="pd-h2">Selectors & control</h2><p class="pd-body">Control keys switch the model and reasoning effort mid-session, collapse tool output, hand off to your editor, interrupt a running turn, and exit. The selectors, tool-collapse, and the <span class="pd-kbd">Esc</span> interrupt stay responsive even while the agent is mid-turn.</p><div class="pd-table-wrap"><table class="pd-table pd-keytable"><thead><tr><th style="width:34%">Key</th><th>Action</th></tr></thead><tbody><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">M</span></td><td>Open the model selector.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">R</span></td><td>Open the reasoning-effort selector.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">O</span></td><td>Collapse / expand all tool-call output.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">G</span></td><td>Edit the current draft in <code>$EDITOR</code>, then read it back.</td></tr><tr><td><span class="pd-kbd">Esc</span></td><td>Interrupt the running agent turn; dismiss an open selector.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">C</span> / <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">D</span></td><td>Exit cleanly.</td></tr></tbody></table></div><div class="pg-callout pg-callout--note"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Selectors are live-only</p>Picking a model (<span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">M</span>) or reasoning level (<span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">R</span>) rebuilds the active provider config and applies it to the running agent for this session — nothing is written back to <a href="configuration.html#models"><code>config.toml</code></a>.</div></div><h2 id="terminal" class="pd-h2">Terminal protocol</h2><p class="pd-body">Two TUI behaviours depend on the terminal’s keyboard protocol. panto negotiates the best available at startup and degrades gracefully.</p><h3 id="terminal-shiftenter" class="pd-h3">Shift + Enter</h3><p class="pd-body">In the bare legacy protocol, <span class="pd-kbd">Enter</span> and <span class="pd-kbd">Shift</span>+<span class="pd-kbd">Enter</span> send the same byte. To tell them apart, panto pushes the <strong>Kitty keyboard protocol</strong> (disambiguate + report-alternates) and queries the terminal; if that’s unavailable it falls back to xterm’s <strong>modifyOtherKeys</strong> mode (which tmux and xterm honour).</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Terminal</th><th>Shift + Enter</th></tr></thead><tbody><tr><td>Kitty, Ghostty, foot (Kitty protocol)</td><td>Newline — fully supported.</td></tr><tr><td>xterm, tmux (modifyOtherKeys)</td><td>Newline — via the fallback.</td></tr><tr><td>macOS Terminal.app (neither)</td><td>Indistinguishable from Enter — submits.</td></tr></tbody></table></div><div class="pg-callout pg-callout--warn"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">No newline in Terminal.app</p>On terminals that support neither protocol, <span class="pd-kbd">Shift</span>+<span class="pd-kbd">Enter</span> can’t be distinguished from <span class="pd-kbd">Enter</span>, so it submits. Use a terminal with the Kitty protocol (Ghostty, Kitty, foot) — or tmux — if you want multi-line input.</div></div><h3 id="terminal-paste" class="pd-h3">Bracketed paste</h3><p class="pd-body">panto enables bracketed paste, so multi-line pastes arrive as a single literal block rather than being re-interpreted as a stream of keypresses (a stray newline in a paste won’t submit your message early).</p><div class="pg-callout pg-callout--note"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">What isn’t here yet</p>The input model reserves space for richer behaviour — key-release events and super/hyper modifiers under the full Kitty protocol — but those aren’t consumed today, so they’re not bindings yet.</div></div><nav class="pd-prevnext"><a class="pd-pn pd-pn--prev" href="configuration.html"><span class="pd-pn__dir">← Previous</span><span class="pd-pn__title">Configuration</span></a><a class="pd-pn pd-pn--next" href="lua.html"><span class="pd-pn__dir">Next →</span><span class="pd-pn__title">Lua extensions</span></a></nav></article></main> +<main class="pd-main"><article class="pd-doc" data-screen-label="Key bindings"><p class="panto-eyebrow pd-eyebrow">Reference</p><h1 class="pd-doc__h1">Key bindings</h1><div class="pd-doc__meta"><span class="pg-badge pg-badge--neutral">TUI input</span></div><p class="pd-lead">The session UI reads raw keystrokes and negotiates richer terminal keyboard protocols at startup. These are the bindings the current TUI recognizes.</p><h2 id="editing" class="pd-h2">Editing & cursor</h2><p class="pd-body">The input box is multi-line and accepts full UTF-8 text. Pasted text arrives as one literal block via bracketed paste.</p><div class="pd-table-wrap"><table class="pd-table pd-keytable"><thead><tr><th style="width:34%">Key</th><th>Action</th></tr></thead><tbody><tr><td>any printable key</td><td>Insert text at the cursor.</td></tr><tr><td><span class="pd-kbd">←</span> / <span class="pd-kbd">→</span></td><td>Move one character.</td></tr><tr><td><span class="pd-kbd">Alt</span>+<span class="pd-kbd">B</span> / <span class="pd-kbd">Alt</span>+<span class="pd-kbd">F</span><br><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">←</span> / <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">→</span></td><td>Move by word. Terminals vary in which encoding they send; panto accepts the common ones.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">A</span> / <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">E</span></td><td>Jump to start / end of the current line.</td></tr><tr><td><span class="pd-kbd">Backspace</span> / <span class="pd-kbd">Delete</span></td><td>Delete backward / forward.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">U</span> / <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">W</span></td><td>Delete to line start / delete previous word.</td></tr><tr><td>paste</td><td>Insert the pasted bytes literally; pasted newlines do not auto-submit.</td></tr></tbody></table></div><h2 id="submit" class="pd-h2">Submit & newline</h2><div class="pd-table-wrap"><table class="pd-table pd-keytable"><thead><tr><th style="width:34%">Key</th><th>Action</th></tr></thead><tbody><tr><td><span class="pd-kbd">Enter</span></td><td>Submit the current input.</td></tr><tr><td><span class="pd-kbd">Shift</span>+<span class="pd-kbd">Enter</span></td><td>Insert a newline when the terminal can report it distinctly.</td></tr></tbody></table></div><p class="pd-body">Whether <span class="pd-kbd">Shift</span>+<span class="pd-kbd">Enter</span> works depends on keyboard-protocol support. panto negotiates Kitty first, then falls back to xterm <code>modifyOtherKeys</code>.</p><h2 id="control" class="pd-h2">Selectors & control</h2><div class="pd-table-wrap"><table class="pd-table pd-keytable"><thead><tr><th style="width:34%">Key</th><th>Action</th></tr></thead><tbody><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">T</span></td><td>Open the model selector.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">R</span></td><td>Open the reasoning selector.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">O</span></td><td>Collapse / expand all tool output.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">G</span></td><td>Edit the current draft in <code>$EDITOR</code> (or <code>[tui] editor</code>).</td></tr><tr><td><span class="pd-kbd">Esc</span></td><td>Interrupt the running turn, close an open selector, or clear the input when idle.</td></tr><tr><td><span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">C</span> / <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">D</span></td><td>Exit cleanly.</td></tr></tbody></table></div><p class="pd-body">Selectors are filterable: type to narrow the list, move with <span class="pd-kbd">↑</span>/<span class="pd-kbd">↓</span> or <span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">N</span>/<span class="pd-kbd">Ctrl</span>+<span class="pd-kbd">P</span>, and accept with <span class="pd-kbd">Enter</span> (or <span class="pd-kbd">Tab</span> for completion-style pickers).</p><div class="pg-callout pg-callout--note"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Selectors are session-local</p>Choosing a model or reasoning level changes the live session only. Nothing is written back to <code>config.toml</code> or <code>models.toml</code>.</div></div><h2 id="terminal" class="pd-h2">Terminal protocol</h2><p class="pd-body">panto enables bracketed paste and probes for keyboard protocol support at startup.</p><h3 id="terminal-shiftenter" class="pd-h3">Shift + Enter</h3><p class="pd-body">With the legacy terminal protocol, <span class="pd-kbd">Enter</span> and <span class="pd-kbd">Shift</span>+<span class="pd-kbd">Enter</span> are the same byte. panto pushes the Kitty keyboard protocol and queries the terminal; if that is unavailable it falls back to xterm <code>modifyOtherKeys</code>.</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Terminal</th><th>Shift + Enter</th></tr></thead><tbody><tr><td>Kitty, Ghostty, foot</td><td>Supported via Kitty keyboard protocol.</td></tr><tr><td>xterm, tmux</td><td>Supported via <code>modifyOtherKeys</code> fallback.</td></tr><tr><td>Terminal.app and similar</td><td>Indistinguishable from Enter, so it submits.</td></tr></tbody></table></div><h3 id="terminal-paste" class="pd-h3">Bracketed paste</h3><p class="pd-body">Bracketed paste means multi-line pastes are inserted literally instead of being reinterpreted as a sequence of keypresses.</p><nav class="pd-prevnext"><a class="pd-pn pd-pn--prev" href="configuration.html"><span class="pd-pn__dir">← Previous</span><span class="pd-pn__title">Configuration</span></a><a class="pd-pn pd-pn--next" href="lua.html"><span class="pd-pn__dir">Next →</span><span class="pd-pn__title">Lua extensions</span></a></nav></article></main> <aside class="pd-toc"><p class="pd-toc__title">On this page</p><ul class="pd-toc__list"><li><a href="#editing" class="pd-toc__link pd-toc__link--active">Editing & cursor</a></li><li><a href="#submit" class="pd-toc__link">Submit & newline</a></li><li><a href="#control" class="pd-toc__link">Selectors & control</a></li><li><a href="#terminal" class="pd-toc__link">Terminal protocol</a></li></ul></aside> </div> </div> @@ -20,44 +20,52 @@ <div class="pd-header__right"><a class="pd-ghlink" href="https://code.tjp.lol/pantograph.git/" target="_blank" rel="noreferrer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 .5 5 .5 5 .5c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 7.5c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg> Source</a></div> </header> <div class="pd-shell"> -<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Tools & extensions</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link pd-navsec__link--active">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> +<aside class="pd-sidebar"><div class="pd-sidebar__scroll"><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/></svg>Getting started</p><ul class="pd-navsec__list"><li><a href="index.html#overview" class="pd-navsec__link">Overview</a></li><li><a href="index.html#install" class="pd-navsec__link">Install</a></li><li><a href="index.html#first-run" class="pd-navsec__link">First run</a></li><li><a href="index.html#authenticate" class="pd-navsec__link">Authenticate</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg>Command line</p><ul class="pd-navsec__list"><li><a href="cli.html#starting" class="pd-navsec__link">Starting a session</a></li><li><a href="cli.html#sessions" class="pd-navsec__link"><code>panto sessions</code></a></li><li><a href="cli.html#models" class="pd-navsec__link"><code>panto models</code></a></li><li><a href="cli.html#auth" class="pd-navsec__link"><code>panto auth</code></a></li><li><a href="cli.html#bootstrap" class="pd-navsec__link"><code>panto bootstrap</code></a></li><li><a href="cli.html#lua" class="pd-navsec__link"><code>panto lua</code></a></li><li><a href="cli.html#slash-commands" class="pd-navsec__link">Slash commands</a></li><li><a href="cli.html#environment" class="pd-navsec__link">Environment</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M3 5h11"/><path d="M19 5h2"/><path d="M3 12h6"/><path d="M14 12h7"/><path d="M3 19h10"/><path d="M18 19h3"/><circle cx="16" cy="5" r="2"/><circle cx="11" cy="12" r="2"/><circle cx="15" cy="19" r="2"/></svg>Configuration</p><ul class="pd-navsec__list"><li><a href="configuration.html#files" class="pd-navsec__link">Files & layers</a></li><li><a href="configuration.html#providers" class="pd-navsec__link"><code>[providers]</code></a></li><li><a href="configuration.html#auth" class="pd-navsec__link"><code>[auth]</code></a></li><li><a href="configuration.html#defaults" class="pd-navsec__link"><code>[defaults]</code></a></li><li><a href="configuration.html#policies" class="pd-navsec__link">Extensions policy</a></li><li><a href="configuration.html#compaction" class="pd-navsec__link"><code>[compaction]</code></a></li><li><a href="configuration.html#models" class="pd-navsec__link"><code>models.toml</code></a></li><li><a href="configuration.html#system-prompt" class="pd-navsec__link">System prompt</a></li></ul></div><div class="pd-navsec"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.001"/><path d="M10 8h.001"/><path d="M14 8h.001"/><path d="M18 8h.001"/><path d="M8 12h.001"/><path d="M12 12h.001"/><path d="M16 12h.001"/><path d="M7 16h10"/></svg>Key bindings</p><ul class="pd-navsec__list"><li><a href="keybindings.html#editing" class="pd-navsec__link">Editing & cursor</a></li><li><a href="keybindings.html#submit" class="pd-navsec__link">Submit & newline</a></li><li><a href="keybindings.html#control" class="pd-navsec__link">Selectors & control</a></li><li><a href="keybindings.html#terminal" class="pd-navsec__link">Terminal protocol</a></li></ul></div><div class="pd-navsec pd-navsec--current"><p class="pd-navsec__title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="1em" height="1em"><path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z"/><path d="m7 16.5-4.74-2.85"/><path d="m7 16.5 5-3"/><path d="M7 16.5v5.17"/><path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z"/><path d="m17 16.5-5-3"/><path d="m17 16.5 4.74-2.85"/><path d="M17 16.5v5.17"/><path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z"/><path d="M12 8 7.26 5.15"/><path d="m12 8 4.74-2.85"/><path d="M12 13.5V8"/></svg>Lua extensions</p><ul class="pd-navsec__list"><li><a href="lua.html#overview" class="pd-navsec__link pd-navsec__link--active">Overview</a></li><li><a href="lua.html#loading" class="pd-navsec__link">Loading extensions</a></li><li><a href="lua.html#tools" class="pd-navsec__link"><code>register_tool</code></a></li><li><a href="lua.html#commands" class="pd-navsec__link"><code>register_command</code></a></li><li><a href="lua.html#events" class="pd-navsec__link">Events: on & emit</a></li><li><a href="lua.html#components" class="pd-navsec__link">Lua components</a></li><li><a href="lua.html#runtime" class="pd-navsec__link">Runtime & luarocks</a></li></ul></div></div></aside> <div class="pd-scrim"></div> -<main class="pd-main"><article class="pd-doc" data-screen-label="Lua extensions"><p class="panto-eyebrow pd-eyebrow">Reference</p><h1 class="pd-doc__h1">Lua extensions</h1><div class="pd-doc__meta"><span class="pg-badge pg-badge--neutral">require("panto")</span></div><p class="pd-lead">Everything beyond the core is a Lua extension — including the standard tools. An extension is a Lua file that calls into the <code>panto.ext</code> table to add tools, slash commands, and UI behaviour. This is how panto stays small without staying useless.</p><h2 id="overview" class="pd-h2">Overview</h2><p class="pd-body">Inside an extension, <code>local panto = require("panto")</code> gives you the API. The <code>panto.ext</code> table has three entry points:</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">register_tool</span><span class="pg-badge pg-badge--neutral">model-facing</span></div><div class="pd-opt__desc">Add a tool the model can call.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">register_command</span><span class="pg-badge pg-badge--neutral">you-facing</span></div><div class="pd-opt__desc">Add a <code>/</code>slash command to the REPL.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">on / emit</span><span class="pg-badge pg-badge--neutral">UI events</span></div><div class="pd-opt__desc">Hook the UI event bus to wrap or replace how things render.</div></div></div><p class="pd-body">All extensions share one long-lived Lua interpreter, so module-global state persists across calls. The <a href="#runtime">runtime</a> includes <code>luv</code> (libuv), so tool handlers can perform async I/O.</p><h2 id="loading" class="pd-h2">Loading extensions</h2><p class="pd-body">Drop a <code>.lua</code> file into an extensions directory and it loads at startup. panto discovers extensions across three layers:</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Layer</th><th>Location</th></tr></thead><tbody><tr><td class="mono">base</td><td class="mono">$PANTO_HOME/agent</td></tr><tr><td class="mono">user</td><td class="mono">$XDG_CONFIG_HOME/panto/extensions/ · else ~/.config/panto/extensions/</td></tr><tr><td class="mono">project</td><td class="mono">./.panto/extensions/</td></tr></tbody></table></div><p class="pd-body">Project shadows user shadows base. Which extensions and tools actually load is gated by the <a href="configuration.html#policies"><code>[extensions]</code> and <code>[tools]</code></a> allow/deny policies.</p><div class="pg-callout pg-callout--warn"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Name collisions abort startup</p>If two surviving extensions register a tool (or command) with the same name, panto refuses to start rather than pick one silently. A Lua command may not shadow a builtin like <code>/compact</code>.</div></div><h2 id="tools" class="pd-h2">register_tool</h2><p class="pd-body">A tool is something the model can call. Describe it with a JSON-Schema for its arguments and a handler that returns a string result.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">name</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">The tool name the model invokes.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">description</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Shown to the model; explain when to use it.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">schema</span><span class="pd-opt__type">table</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">A JSON-Schema table (<code>type = "object"</code>, <code>properties</code>, <code>required</code>) describing the arguments.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">handler</span><span class="pd-opt__type">function(input)</span></div><div class="pd-opt__desc">Receives the decoded arguments table; returns a string result. May yield (async).</div></div></div><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">.panto/extensions/echo.lua</span><span class="pg-code__lang">lua</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>local panto = require("panto") - -panto.ext.register_tool { +<main class="pd-main"><article class="pd-doc" data-screen-label="Lua extensions"><p class="panto-eyebrow pd-eyebrow">Reference</p><h1 class="pd-doc__h1">Lua extensions</h1><div class="pd-doc__meta"><span class="pg-badge pg-badge--neutral">require("panto")</span></div><p class="pd-lead">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.</p><h2 id="overview" class="pd-h2">Overview</h2><p class="pd-body">Extension source files are evaluated first and activated later. A file must return one of these shapes:</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">{ name, activate }</span></div><div class="pd-opt__desc">A named entry whose <code>activate()</code> function performs registration when the entry survives policy and shadowing.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">{ name, description, schema, handler }</span></div><div class="pd-opt__desc">Sugar form for a single tool entry. Activation registers it as a tool automatically.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">{ ... , ... }</span></div><div class="pd-opt__desc">A list of entries or tool declarations returned from one file.</div></div></div><p class="pd-body">Inside <code>activate()</code>, use <code>local panto = require("panto")</code> and the <code>panto.ext</code> table:</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">register_tool</span></div><div class="pd-opt__desc">Register a model-facing tool.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">register_command</span></div><div class="pd-opt__desc">Register a slash command.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">on / emit</span></div><div class="pd-opt__desc">Subscribe to transcript/UI lifecycle events or fire custom ones.</div></div></div><h2 id="loading" class="pd-h2">Loading extensions</h2><p class="pd-body">panto scans both <code>extensions/</code> and <code>tools/</code> directories at each layer:</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Layer</th><th>Location</th></tr></thead><tbody><tr><td class="mono">base</td><td class="mono">$XDG_DATA_HOME/panto/agent/{extensions,tools}</td></tr><tr><td class="mono">user</td><td class="mono">$XDG_CONFIG_HOME/panto/{extensions,tools} · else ~/.config/panto/{extensions,tools}</td></tr><tr><td class="mono">project</td><td class="mono">./.panto/{extensions,tools}</td></tr></tbody></table></div><p class="pd-body">Additional sources can come from <code>[extensions] paths</code> and <code>[extensions] rocks</code>. Cross-layer precedence is project > user > base. Within one layer, canonical directories beat extra paths, which beat rocks. Identity is the returned entry's <code>name</code>, not the filename.</p><div class="pg-callout pg-callout--warn"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">Eval is side-effect free</p>Every candidate file is evaluated before policy and shadowing are resolved, including entries that will later be denied. Put side effects in <code>activate()</code>, not at top level.</div></div><h2 id="tools" class="pd-h2">register_tool</h2><p class="pd-body">The simplest way to define a tool is to return the sugar form directly:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">.panto/tools/echo.lua</span><span class="pg-code__lang">lua</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>return { name = "echo", description = "Echo back the given message.", schema = { type = "object", properties = { - message = { type = "string", description = "The text to echo back." }, + message = { type = "string" }, }, required = { "message" }, }, handler = function(input) return "echo: " .. input.message end, -}</code></pre></div></div><h2 id="commands" class="pd-h2">register_command</h2><p class="pd-body">A slash command runs locally when you type <code>/name</code> in the REPL. The handler runs synchronously and acts by side effect (typically writing output); its return value is ignored.</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">name</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Command name, without the leading <code>/</code>.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">description</span><span class="pd-opt__type">string</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">One-line summary (for listings).</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">handler</span><span class="pd-opt__type">function(args)</span></div><div class="pd-opt__desc"><code>args</code> is the trimmed text after the command name — the empty string when none was given.</div></div></div><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">.panto/extensions/greet.lua</span><span class="pg-code__lang">lua</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>local panto = require("panto") - -panto.ext.register_command { +}</code></pre></div></div><p class="pd-body">Use <code>register_tool</code> inside <code>activate()</code> when you want one entry to register multiple tools or commands together.</p><h2 id="commands" class="pd-h2">register_command</h2><p class="pd-body">Slash commands are typically registered from an explicit entry:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">.panto/extensions/greet.lua</span><span class="pg-code__lang">lua</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>return { name = "greet", - description = "Print a greeting. Optional args name who to greet.", - handler = function(args) - local who = args ~= "" and args or "world" - io.write("\n[greet] hello, " .. who .. "!\n") + 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, -}</code></pre></div></div><h2 id="events" class="pd-h2">Events: on & emit</h2><p class="pd-body"><code>panto.ext.on(name, handler)</code> subscribes a handler to the UI event bus — the same bus the built-in TUI uses. Handlers fire in registration order, and can wrap or replace the component that renders a given moment of the transcript.</p><p class="pd-body">The handler receives an event object <code>e</code> that is valid <strong>only</strong> during the call (copy any field you need into a local — don’t read <code>e</code> later at render time):</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">e.name</span><span class="pd-opt__type">string</span></div><div class="pd-opt__desc">The event name.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">e.<field></span></div><div class="pd-opt__desc">Read-only payload fields (nil when absent, so you can branch on them).</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">e:getComponent()</span></div><div class="pd-opt__desc">The current native component, as an opaque pass-through handle.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">e:setComponent(c)</span></div><div class="pd-opt__desc">Replace the component for this boundary — pass the handle back to wrap it, or a Lua component table to replace it.</div></div></div><p class="pd-body">The events and the payload fields they carry:</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Event</th><th>Payload fields</th></tr></thead><tbody><tr><td class="mono">tool</td><td class="mono">index, tool_name, id, delta, input, output</td></tr><tr><td class="mono">thinking</td><td class="mono">index, delta, text</td></tr><tr><td class="mono">assistant_text</td><td class="mono">index, delta, text</td></tr><tr><td class="mono">user_message</td><td class="mono">text</td></tr><tr><td class="mono">session_start</td><td class="mono">version, cwd, model</td></tr><tr><td class="mono">compaction</td><td class="mono">summary</td></tr><tr><td class="mono">custom</td><td class="mono">— produced by emit</td></tr></tbody></table></div><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">.panto/extensions/skill-badge.lua</span><span class="pg-code__lang">lua</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>local panto = require("panto") - --- Change how the "skill" tool renders in the transcript. -panto.ext.on("tool", function(e) - if e.tool_name ~= "skill" then return end -- ignore every other tool - local name = e.tool_name -- snapshot now: e is short-lived - e:setComponent({ - render = function(self, width) - return { "▸ running skill: " .. name } - end, - }) -end)</code></pre></div></div><p class="pd-body"><code>panto.ext.emit(name, data)</code> fires a custom event on the same bus, running both native and Lua handlers. Today <code>data</code> is surfaced as an opaque <code>custom</code> payload.</p><h2 id="components" class="pd-h2">Lua components</h2><p class="pd-body">A Lua component is a table you hand to <code>e:setComponent</code>. The one required method is <code>render</code>:</p><div class="pd-opts"><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">render</span><span class="pd-opt__type">function(self, width)</span><span class="pg-badge pg-badge--danger">required</span></div><div class="pd-opt__desc">Return an array of strings — one per line. Runs synchronously (it may <em>not</em> yield), each line is truncated to <code>width</code> columns, and any error becomes a safe fallback line so the frame never crashes. An empty array is valid (zero lines).</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">handleInput</span><span class="pd-opt__type">function(self, data)</span></div><div class="pd-opt__desc">Optional. Handle input routed to the component.</div></div><div class="pd-opt"><div class="pd-opt__key"><span class="pd-opt__name">firstLineChanged / invalidate</span><span class="pd-opt__type">function</span></div><div class="pd-opt__desc">Optional render-cache hooks.</div></div></div><h2 id="runtime" class="pd-h2">Runtime & luarocks</h2><p class="pd-body">panto embeds a pinned Lua and luarocks, and ships exactly one battery: <code>luv</code> (libuv’s event loop), the single runtime dependency. It drives panto’s coroutine scheduler and gives extensions one rich async-I/O surface. The bootstrap reconciles the installed rocks tree against panto’s manifest on every startup — installing what’s missing, removing what’s stale.</p><p class="pd-body">Anything else is yours to add. Install extra rocks into panto’s tree with the embedded luarocks, via <a href="cli.html#lua"><code>panto lua</code></a>:</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>panto lua -e 'arg[0]="luarocks"; require("luarocks.cmd").run_command("install","lpeg")'</code></pre></div></div><div class="pg-callout pg-callout--tip"><span class="pg-callout__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"/></svg></span><div class="pg-callout__body"><p class="pg-callout__title">One-time setup</p>On a fresh machine, run <a href="cli.html#bootstrap"><code>panto bootstrap</code></a> once to build the rocks tree before your extensions need it.</div></div><nav class="pd-prevnext"><a class="pd-pn pd-pn--prev" href="keybindings.html"><span class="pd-pn__dir">← Previous</span><span class="pd-pn__title">Key bindings</span></a><span style="flex:1"></span></nav></article></main> +}</code></pre></div></div><h2 id="events" class="pd-h2">Events: on & emit</h2><p class="pd-body"><code>panto.ext.on(name, handler)</code> subscribes to transcript/UI lifecycle events. The handler receives an <code>event</code> userdata with snake-case methods <code>get_component()</code> and <code>set_component()</code>, plus payload fields such as <code>tool_name</code>, <code>delta</code>, <code>text</code>, <code>id</code>, <code>input</code>, <code>output</code>, <code>cwd</code>, and <code>model</code>.</p><div class="pd-table-wrap"><table class="pd-table"><thead><tr><th>Event family</th><th>Names</th></tr></thead><tbody><tr><td class="mono">tool</td><td class="mono">tool, tool_details, tool_delta, tool_call_complete, tool_result, tool_collapse</td></tr><tr><td class="mono">thinking</td><td class="mono">thinking, thinking_delta, thinking_complete</td></tr><tr><td class="mono">assistant text</td><td class="mono">assistant_text, assistant_text_delta, assistant_text_complete</td></tr><tr><td class="mono">single-shot</td><td class="mono">user_message, session_start, compaction</td></tr><tr><td class="mono">custom</td><td class="mono">whatever you emit via <code>panto.ext.emit</code></td></tr></tbody></table></div><p class="pd-body">A few payload fields are writable during dispatch: <code>user_message.text</code>, <code>tool_call_complete.input</code> (assign a Lua table; panto serializes it to JSON), and <code>tool_result.output</code>.</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">.panto/extensions/skill-badge.lua</span><span class="pg-code__lang">lua</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>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, +}</code></pre></div></div><h2 id="components" class="pd-h2">Lua components</h2><p class="pd-body">A Lua component is a table passed to <code>set_component()</code>. The required method is <code>render(self, width)</code>, which returns an array of strings. <code>handleInput(self, data)</code> is optional. Differential repaint is cache-derived automatically; you do not need to implement <code>firstLineChanged</code> yourself.</p><h2 id="runtime" class="pd-h2">Runtime & luarocks</h2><p class="pd-body">panto embeds a pinned Lua and luarocks runtime and ships <code>luv</code> as its core async dependency. Extra rocks may be loaded as extension sources via <code>[extensions] rocks</code> and installed with <code>panto update</code>, or added manually with <code>panto lua</code>.</p><div class="pg-code"><div class="pg-code__bar"><span class="pg-code__name">shell</span><span class="pg-code__lang">bash</span><button class="pg-code__copy" type="button" aria-label="Copy code"><svg class="pg-code__copyglyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><svg class="pg-code__check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m20 6-11 11-5-5"/></svg><span class="pg-code__label">Copy</span></button></div><div class="pg-code__scroll"><pre><code>panto update +panto lua -e 'arg[0]="luarocks"; require("luarocks.cmd").run_command("install","lpeg")'</code></pre></div></div><nav class="pd-prevnext"><a class="pd-pn pd-pn--prev" href="keybindings.html"><span class="pd-pn__dir">← Previous</span><span class="pd-pn__title">Key bindings</span></a><span style="flex:1"></span></nav></article></main> <aside class="pd-toc"><p class="pd-toc__title">On this page</p><ul class="pd-toc__list"><li><a href="#overview" class="pd-toc__link pd-toc__link--active">Overview</a></li><li><a href="#loading" class="pd-toc__link">Loading extensions</a></li><li><a href="#tools" class="pd-toc__link">register_tool</a></li><li><a href="#commands" class="pd-toc__link">register_command</a></li><li><a href="#events" class="pd-toc__link">Events: on & emit</a></li><li><a href="#components" class="pd-toc__link">Lua components</a></li><li><a href="#runtime" class="pd-toc__link">Runtime & luarocks</a></li></ul></aside> </div> </div> |
