Voice
Jarvis Ui
JARVIS UI is a plugin-based terminal UI framework that gives any AI agent a common interface instead of each one reinventing the same plumbing. Every agent ends up solving the same four problems: discovering tools, registering UI components, managing a plugin lifecycle, and dispatching calls.
Install
pip install .
README
# JARVIS UI
A plugin-based terminal UI framework that lets **any** AI agent plug into a common interface.
Write your agent's tools and UI components once; JARVIS handles discovery, lifecycle, and dispatch. The core is **standard library only** — it adds no dependencies to your agent's environment.
```
plugins discover -> load -> get_tools() / get_ui_components() -> dispatch
```
---
## Why
Every agent ends up reinventing the same plumbing: a way to discover tools, register UI components, manage a plugin lifecycle, and dispatch calls. This framework is that plumbing, and nothing else. Your agent supplies the domain logic.
## Status
Alpha. The core and all three bundled example plugins load and execute. The interface may still change.
---
## Install
```bash
git clone https://github.com/vinodrajvtr/jarvis-ui.git
cd jarvis-ui
python3 main.py --help
```
Or as a package:
```bash
pip install .
```
Requires Python 3.8+. No third-party dependencies.
---
## Quick start
Run the bundled examples:
```bash
# See what's available
python3 main.py tools list
python3 main.py ui list
# Load an agent plugin (note: --plugin-dir points at the PARENT folder)
python3 main.py --plugin-dir examples/hermes-plugin plugins load hermes
```
Expected output:
```
Registered tool: hermes_chat (from hermes)
Registered UI component: hermes_query_input (from hermes)
Plugin loaded: hermes v0.1.0
Successfully loaded: hermes v0.1.0
Tools: 3
UI Components: 4
```
---
## Add your own agent
### 1. Create the directory layout
The loader looks for `<plugin-dir>/<PLUGIN_NAME>/plugin.py`. The **folder name becomes the plugin name**, and `--plugin-dir` points at its **parent**.
```
my-agent-plugin/ <- point --plugin-dir here
my-agent/ <- folder name = plugin name
plugin.py <- filename must be exactly this
```
### 2. Write the plugin
```python
import os
import sys
# core/ holds plugin_manager.py. Put it on sys.path so the flat import
# resolves -- there is no `jarvis_ui` package to import from.
sys.path.insert(
0, os.path.join(os.path.dirname(__file__), "..", "..", "core")
)
from plugin_manager import PluginInterface
class MyAgentPlugin(PluginInterface):
def __init__(self):
self.name = "my-agent"
self.version = "0.1.0"
self.context = None
def on_load(self, agent_context):
"""Called when the plugin loads. agent_context carries config, keys,
model preferences, workspace path."""
self.context = agent_context
def on_unload(self):
"""Release connections, threads, handles."""
self.context = None
def get_tools(self):
return [
{
"name": "my_agent_ask",
"description": "Send a prompt to MyAgent",
"parameters": {"prompt": "string"},
"handler": self._ask,
}
]
def get_ui_components(self):
return [
{
"name": "my_agent_input",
"type": "input",
"label": "MyAgent:",
"placeholder": "Enter query...",
"handler": self._ask,
}
]
def get_config(self):
return {"setting": "default"}
def validate_config(self, config):
return isinstance(config, dict)
def _ask(self, prompt=""):
# Replace this with a real call into your agent.
return {"success": True, "result": prompt}
```
### 3. Load it
```bash
python3 main.py --plugin-dir my-agent-plugin plugins load my-agent
```
```
Registered tool: my_agent_ask (from my-agent)
Registered UI component: my_agent_input (from my-agent)
Successfully loaded: my-agent v0.1.0
Tools: 1
UI Components: 1
```
That's it. No registration call, no manifest, no framework edits.
---
## Interface
| Method | Purpose |
|---|---|
| `on_load(agent_context)` | Initialize; receives config, keys, workspace |
| `on_unload()` | Clean up resources |
| `get_tools()` | Return tool definitions (`name`, `description`, `parameters`, `handler`) |
| `get_ui_components()` | Return component definitions (`name`, `type`, `label`, `handler`) |
| `get_config()` | Return default configuration |
| `validate_config(config)` | Return `True` if config is usable |
Component `type` is one of `input`, `output`, `button`, `selector`.
---
## CLI
```
python3 main.py [--plugin-dir DIR] <command>
plugins list | discover | load <name> | unload <name> | reload [name]
tools list | execute <tool> [json-params]
ui list | create <type> [--label ...] [--placeholder ...]
```
Global `--plugin-dir` sets where plugins are discovered from. It defaults to
`core/plugins/`, which is usually not where yours live — pass it explicitly.
---
## Example plugins
| Plugin | Location | Shows |
|---|---|---|
| hermes | `examples/hermes-plugin/hermes/` | Talking to a Hermes agent over its API |
| openclaw | `examples/openclaw-plugin/openclaw/` | Wrapping a CLI-driven agent |
| claude-code | `examples/claude-code-plugin/claude-code/` | Subprocess integration with availability checks |
Each declares 3 tools and 4 UI components.
---
## Notes and gotchas
- **`--plugin-dir` points at the parent.** Passing the plugin folder itself finds nothing.
- **The import is flat** (`from plugin_manager import PluginInterface`), not `from jarvis_ui.core...`. `core/` goes on `sys.path`.
- **There is no `register_plugin()`.** Discovery is automatic: the loader imports `plugin.py` and instantiates the `PluginInterface` subclass it finds.
- **Name matching is normalized.** Non-alphanumerics are stripped and case is folded, so a `my-agent` folder matches a `MyAgentPlugin` class.
- **Subclass `PluginInterface`.** `load_plugin` validates with `isinstance()`, so a duck-typed class that doesn't subclass it will be rejected.
- **Set `name`/`version` in `__init__`.** They're read from the instance after construction.
---
## Project layout
```
jarvis-ui/
core/ plugin_manager, tool_integration, ui_components
examples/ three working agent plugins
docs/framework_docs.md full API reference
main.py CLI entry point
jarvis_tui.py curses front-end for the hermes plugin
test_plugin.py smoke test
```
---
## License
Apache-2.0. See [LICENSE](LICENSE).
voice
Comments
Sign in to leave a comment