pi-dstui
Lisp DSL runtime for composable, on-demand TUI components
Package details
Install pi-dstui from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-dstui- Package
pi-dstui- Version
0.1.1- Published
- May 29, 2026
- Downloads
- not available
- Author
- unitdhda
- License
- unknown
- Types
- extension, skill
- Size
- 51.1 KB
- Dependencies
- 0 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
],
"skills": [
"./.pi/skills"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
tui
https://github.com/user-attachments/assets/4e62e1ad-fd89-4266-b615-a0d0a344da12
Rich, interactive terminal UI components for pi agent
— defined in a small Lisp DSL, persisted to disk, and rendered as full-screen overlays on demand.
Most agent interactions collapse to text: the agent asks a question, you type an answer, and both sides guess at structure. tui replaces that back-and-forth with purpose-built input widgets. Instead of typing "option 2" in response to a numbered list, you navigate a radio picker. Instead of describing a number with a note about the valid range, you turn a rotary dial. The agent gets a typed value; you get a clear, keyboard-driven UI.
This makes tui a single replacement for question-prompt plugins, checklist plugins, checkbox plugins, and any other extension whose job is structured input — while keeping the entire widget definition inside the conversation context as a tiny Lisp source file.
Install
pi install npm:tui
Or for a project-local install:
pi install -l npm:tui
After install, pi loads the extension automatically. No configuration needed.
Use cases
Confirmation dialogs
Replace yes/no text prompts with a keyboard-navigable confirm widget. The agent defines it once and reuses it anywhere a destructive action needs approval.
Radio and checklist pickers
Replace numbered-list answers with a real picker. The agent passes a list of options; you select with arrow keys and confirm with Enter. The return value is the chosen item, not a string to parse.
Multi-step forms
Chain multiple overlays to collect structured input across several fields — filenames, environment names, feature flags — without a free-text round-trip for each one.
Progress and status displays
Render progress bars, gauges, and live-updating dashboards during long-running tasks. Timers drive re-renders without waiting for a new tool call.
Domain-specific controls
When a project has a recurring input pattern — selecting from a config enum, adjusting a numeric threshold, picking a deployment target — the agent can define a custom component for it once and save it to .pi/ui-components/. Every subsequent conversation in that project gets the control for free.
pi tools
Installing tui registers two tools in the pi agent.
tui_define_component
Saves a Lisp DSL module to .pi/ui-components/<name>.lisp and registers it in the runtime. The agent calls this to create or update a component definition.
{
"source": "(defcomponent confirm (message) ...)",
"scope": "local",
"module": "confirm"
}
| Parameter | Description |
|---|---|
source |
Full Lisp module source — one or more defcomponent / defview forms |
scope |
"local" (project, .pi/ui-components/) or "global" (~/.pi/agent/ui-components/). Default: "local" |
module |
File/module name. Inferred from the first defcomponent or defview name if omitted |
Global components are available in every project. Local components are scoped to the current working directory.
tui_create_dynamic_ui
Launches a saved component by name as a full-screen overlay and returns the emitted value to the agent.
{
"componentId": "confirm-delete-123",
"type": "confirm",
"title": "Confirm deletion",
"config": { "message": "Delete production database?" }
}
| Parameter | Description |
|---|---|
type |
Component name as registered by tui_define_component |
config |
Props object — keys map to the component's param list |
title |
Displayed in the overlay header |
componentId |
Unique identifier for this invocation |
Returns the value passed to (emit ...) in the DSL, or null if the user dismisses with Escape / Ctrl+C.
Hotload: the component's source file is re-read from disk on every tui_create_dynamic_ui call. Edit a .lisp file and the next invocation picks up the change without restarting the agent.
Skill
Installing tui also registers the tui-runtime-components skill, which gives the agent:
- Full DSL syntax reference and layout primitives
- A component catalog with ready-to-use patterns (radio list, rotary encoder, progress gauge)
- Guidance on when to define a new component vs. reuse a saved one
The agent uses the skill automatically when writing DSL source — you do not need to instruct it.
Hotload
Every tui_create_dynamic_ui call re-reads the component's source file from disk before instantiation:
.pi/ui-components/
confirm.lisp ← edit here, runs immediately on next call
my-picker.lisp
rotary-encoder.lisp
The load order:
- Look up the component name in the in-memory index
- Re-read its
.lispfile from disk and re-compile - If not yet registered, scan
localthenglobaldirectories for<name>.lisp - Instantiate the freshly compiled
ComponentDef
This means the agent can call tui_define_component to write a new version of a component, then immediately call tui_create_dynamic_ui to run it — all within the same message.
Bundled components
The package ships three ready-to-use components in .pi/ui-components/:
| Component | Description |
|---|---|
radio-list |
Single-select list with keyboard navigation and optional pre-selection |
rotary-encoder |
Numeric dial with configurable min, max, step, and initial value |
progress-gauge |
Animated progress display driven by a timer |
The agent can use these directly or use them as templates when defining new ones.
Writing your own components
See docs/dsl.md for the full DSL reference — layout primitives, state, bindings, timers, control flow, and built-in functions.
Programmatic API
The runtime is also importable as a library for use in other pi extensions or tools:
import { compileModule, instantiate, ModuleLoader } from "tui";
See docs/api.md for the full API reference.
Package structure
index.ts pi extension entry — registers tui_define_component and tui_create_dynamic_ui
src/
parser.ts Lisp S-expression parser
runtime.ts DSL evaluator, layout engine, component instantiation
loader.ts ModuleLoader — persistence and hotload
runner.ts makeUiRunner — pi-tui overlay adapter
index.ts Public API exports
.pi/
skills/
tui-runtime-components/ Skill loaded by pi agent
ui-components/ Saved component files (created at runtime)
docs/
dsl.md DSL language reference
api.md Programmatic API reference