luvus-module
GitHub用于编写 luvus 模块,通过 TOML 配置声明 argv 命令以扩展 AI 编码代理功能。支持侧边栏、状态栏、事件钩子、右键菜单及面板等交互组件的开发与调试。
Trigger Scenarios
Install
npx skills add RizRiyz/luvus --skill luvus-module -g -y
SKILL.md
Frontmatter
{
"name": "luvus-module",
"description": "Write a luvus module (an extension for luvus, mission control for your AI coding agents). Use when the user is building or debugging a luvus module: authoring luvus-module.toml, adding a sidebar dock, Luvus Bar widget, right-click action, event hook, module pane, module settings, or calling luvus back over its UHP."
}
Writing a luvus module
A luvus module is a plugin for luvus, mission control for your AI coding agents. There is no SDK and no scripting engine: a module is a directory with a luvus-module.toml manifest that declares argv commands (any executable: sh, python, node, a compiled binary). luvus runs those commands as subprocesses with LUVUS_* context in the environment, and the command calls luvus back through the same UHP the luvus CLI uses. So a module in bash/python/node can do what a built-in feature does.
Reference material in this repo (read for depth): MODULE-GUIDE.md, docs/13-modules.md, and the worked modules in examples/modules/ (branch-dock = a sidebar dock, agent-ping = an event hook, scratch-pane = a pane).
The manifest: luvus-module.toml
Top-level keys (all required unless noted):
id = "yourname.my-module" # unique, reverse-dns style
name = "My Module"
version = "0.1.0"
min_luvus_version = "0.8.3" # oldest luvus this needs
description = "One line." # optional
platforms = ["macos", "linux"] # optional; omit = all. Also per-item.
Then any of these tables, each declaring an argv command (a list, run as-is, cwd = the module dir):
[[docks]]id,title,placement(sidebar.left|sidebar.right) — reserve a sidebar dock. luvus renders it; you fill it withui.dock.push(see below).[[bars]]id,title,region(top-right|bottom-right), optionalpriority— reserve a bounded one-row Luvus Bar widget. Publish structured segments withluvus bar push(ui.bar.pushon the UHP); live content is not persisted.[[startup]]command— run once when the session is up and the socket is listening (and on enable). Dock rows and bar content are not persisted, so this is how you repaint them after a restart.[[events]]on,command— run when a luvus event fires. Validonvalues includeworkspace.created/closed,tab.created/closed/moved,pane.created/closed/forked/moved,pane.agent_status_changed,agent.hook, and thetask.*/lease.*events (seeKNOWN_EVENTSinsrc/module/manifest.rsfor the full set — an unknownonis a hard manifest error).[[actions]]id,title,command, optionalcontexts— a runnable action. Withcontexts = ["pane"|"workspace"|"node"|"agent"|"tab"]it also appears in that right-click menu, acting on what was clicked. Withoutcontextsit is CLI-only (luvus module run <id> <action>). Dock rows also invoke an action on click.[[panes]]id,title,command,placement(split|overlay|tab) — a real pane running your command (luvus module pane open <id> <entrypoint>).[[settings]]key,title,type(bool|string|number|enum), plusdefault,options(enum),min/max/step(number),secret(mask + hide the value). Rendered in Settings → Modules; values reach every command as env (below).
What your command receives (no JSON parsing needed)
luvus puts context in the environment, flat, so a bash module never parses JSON:
LUVUS_MODULE_ID,LUVUS_MODULE_ROOT(the module dir),LUVUS_MODULE_VERSIONLUVUS_MODULE_CONFIG_DIR,LUVUS_MODULE_STATE_DIR— writable per-module dirsLUVUS_WORKSPACE_ID,LUVUS_WORKSPACE_CWD,LUVUS_TAB_INDEXLUVUS_PANE_ID,LUVUS_PANE_CWD,LUVUS_PANE_AGENT,LUVUS_PANE_STATUS(the clicked/target pane)LUVUS_SETTING_<KEY>for each declared setting (uppercased key), plus the whole set as JSON- Dock-row clicks add
LUVUS_MODULE_DOCK_ID,LUVUS_MODULE_ROW_ACTION,LUVUS_MODULE_ROW_VALUE,LUVUS_MODULE_ROW_TEXT,LUVUS_MODULE_ROW_INDEX - Bar-segment clicks add
LUVUS_MODULE_BAR_ID,LUVUS_MODULE_BAR_SEGMENT, and optionalLUVUS_MODULE_BAR_VALUE LUVUS_MODULE_CONTEXT_JSON— the full snapshot, if you want structured dataLUVUS_SOCKET_PATH,LUVUS_BIN_PATH— to call luvus back (below)
What your command can do: call luvus back
Run the luvus CLI from inside the command; it talks to the running server over $LUVUS_SOCKET_PATH. Use "$LUVUS_BIN_PATH" to guarantee the same binary as the session. Module-facing methods:
luvus ui dock push --id <dock> --rows <json>(or pipe the JSON on stdin) — fill your dock. Rows are{text, action?, value?}; a row'sactioninvokes one of your[[actions]]on click, with the row'svalueinLUVUS_MODULE_ROW_VALUE.luvus bar push --id <widget> --content <json>— atomically publish structuredtext,symbol,state,badge,progress,spacer, andseparatorsegments. Add--compact-content; anactionmust name one of your[[actions]].luvus ui notification push --text <text> --level info|success|warning|error— publish bounded transient status; use--dedupe-keyfor replacement.luvus ui toast "<text>"— flash a one-line confirmation.luvus ui sidebar/ui dock list/ui dock move— sidebar/dock control.luvus tab rename <name>/tab list— tabs.luvus module settings <id>/module settings <id> <key>— read your settings exactly (values are masked insettings listwhensecret, but exact insettings get).- Plus the whole CLI:
luvus pane split/run/send,luvus agent ...,luvus git ..., etc.
Recipes
A sidebar dock (like examples/modules/branch-dock): declare [[docks]], a [[startup]] that runs a script which builds a JSON rows array and calls ui dock push, and [[events]] so it refreshes on workspace.created/tab.created. Give rows an action matching a [[actions]] id to make them clickable.
A Luvus Bar widget (like examples/modules/ci-bar): declare [[bars]], publish full and compact structured content from a one-shot startup/event action, and keep expensive work outside the render path. Use a pane for arbitrary terminal UI and a dock for multi-row content.
An event hook (like agent-ping): a [[events]] with on = "pane.agent_status_changed" and a command that reads LUVUS_PANE_* and reacts (notify, log, ui toast).
A right-click action: an [[actions]] with contexts = ["pane"] (or workspace/node/agent/tab). It appears in that menu and runs against the clicked target's LUVUS_* env.
A pane: a [[panes]] entry; open it with luvus module pane open <module-id> <entrypoint>.
Test loop
luvus module link <path-to-module-dir> # register a local module (dev)
luvus module list # confirm it's runnable
luvus module log [<id>] # tail command output/errors while iterating
luvus module run <id> <action> # invoke an action directly
luvus module enable <id> | disable <id>
Iterate: edit the script, re-run the action or trigger the event, watch module log. The manifest is validated on link; a broken manifest keeps the entry visible but not runnable, with the reason in module info <id>.
Conventions
- One module, one job. Keep commands fast and quiet — luvus caps a command's output (64 KiB) and runs at most 32 at a time.
- Identity env (
LUVUS_MODULE_ID, socket path) is injected and cannot be overridden. - Use
platformson the manifest or any item to skip a build step / hook / pane / action where it does not apply. - To share it:
luvus module install <owner>/<repo>clones + builds + registers from GitHub; tag the repo with theluvus-moduletopic soluvus module searchfinds it.
Version History
- 9ee6401 Current 2026-08-27 09:04


