AGENT SKILL

sero-plugin

Create Sero plugins — self-contained packages with a Pi extension (agent tools/commands), an optional React UI loaded via Module Federation, and an optional background runtime. Use when the user asks to create a Sero plugin/app/extension, add a sidebar panel, dashboard widget, or agent-integrated feature, or convert a Pi extension into a Sero plugin. Trigger on phrases like "create a plugin", "new Sero app", "add a tool with UI", "build an extension", "convert this Pi extension", or any request that touches `plugins/sero-*-plugin/`.

License 许可
Apache-2.0
所属仓库
sero-labs/sero
最近更新
2026-11-10
Stars
★ 28
Forks
4

01 前言 Front Matter

name
sero-plugin
description
Create Sero plugins — self-contained packages with a Pi extension (agent tools/commands), an optional React UI loaded via Module Federation, and an optional background runtime. Use when the user asks to create a Sero plugin/app/extension, add a sidebar panel, dashboard widget, or agent-integrated feature, or convert a Pi extension into a Sero plugin. Trigger on phrases like "create a plugin", "new Sero app", "add a tool with UI", "build an extension", "convert this Pi extension", or any request that touches `plugins/sero-*-plugin/`.

02 资源文件 18

03 SKILL.md 正文

Building Sero Plugins

A Sero plugin can expose up to three coordinated surfaces, all backed by a single JSON state file:

  • Pi extension (extension/) — standard Pi. Pi-CLI-safe, no Sero imports. Registers tools and commands.
  • Web UI (ui/) — optional React UI loaded via Module Federation. Sero-only.
  • Background runtime (runtime/) — optional. Sero-only. Long-lived workspace orchestration.
                    state.json (source of truth)
            ┌──────────────┼──────────────┐
       Pi extension     Web UI     Background runtime
       (Pi-CLI safe)   (Sero only)    (Sero only)

Sero auto-discovers plugins/sero-*-plugin/ directories with a sero.app manifest in package.json. No manual registry edits.

When to read reference files

Reference When to read
example/sero-notes-plugin/ Start here. Canonical, minimal plugin that exercises every surface (extension + CLI bridge, background runtime, UI hooks, static + dynamic widgets, MF config). Copy and rename it as your starting point. See example/README.md for the file map.
references/templates.md Creating any file (package.json, extension, runtime, UI, Vite, styles). Also contains the Quick do/don't guide and mini examples.
references/api-and-widgets.md Using app-runtime hooks, background runtimes, dashboard widgets, manifest fields
references/conversion-guide.md Converting an existing Pi extension into a plugin

Core rules

UI → plugin behavior

If the UI must trigger plugin-owned logic (sign in, refresh, mutate state, call an API), register a normal plugin tool and invoke it from React:

const { run } = useAppTools();
await run('myapp_auth', { action: 'login' });

Never add a plugin-specific host bridge (window.sero.myPlugin.*, custom preload/IPC channels). Declare the host seams you actually use in sero.plugin.requiredHostCapabilities:

Capability Declare when
appAgent.invokeTool UI uses useAppTools() or window.sero.appAgent.invokeTool(...)
tool.cli Plugin uses bridgeTools, custom cli metadata, or cli.overrideBuiltin
appRuntime.background Plugin declares sero.app.runtime

Tool bridging (AD-020)

App tools are bridged into the single sero-cli tool — always use pi.registerTool().

  • Plugins bridge all tools to sero <tool> ... by default
  • sero.plugin.bridgeTools: omit/true = bridge all, false = none, string[] = selected tool names. A bridged tool is reached as a sero command, so its schema stays out of every session's start-up. List a tool here when the agent uses it only now and then
  • sero.plugin.toolSessionKinds: { "<tool>": ["chat" | "subagent" | "member"] } limits a tool to those kinds of session. A tool with no entry is for every kind
  • A tool's description and cli.summary must say what the tool or service is, not only its one job. The agent cannot assume it knows your plugin
  • sero.plugin.uiOnlyTools: string[] of tools only your UI calls through appAgent.invokeTool. Leave a UI-only tool out of bridgeTools and register it with exposure: 'hidden'; the host enforces the same for every declared name. Its actions count as the user's, so a model that could call it would be able to raise a limit the user set
  • Custom CLI help / summary / raw-args parsing goes on the tool's cli field
  • cli.overrideBuiltin: true only when intentionally replacing a builtin command
  • Never register app tools as customTools in createAgentSession()
  • Bridged tools run against the current session's extension — don't capture registration-scoped pi state in tool logic
  • If a tool exposes CLI subcommands, keep them self-explanatory and consistent with any structured tool actions; support aliases when needed and return explicit errors for unknown subcommands rather than silently falling back to another action

Prompt templates, skills, and slash-command naming

  • Plugin-owned prompt templates and skills are manifest-driven. Declaring prompts/ or skills/ folders is not enough by itself.
  • Add prompt templates to pi.prompts in package.json (for example "./prompts/myapp.md").
  • Add plugin skills to pi.skills in package.json (for example "./skills").
  • If a bridged tool should also have a same-name slash shortcut (for example /mcp for the bridged mcp tool), prefer a prompt template declared in pi.prompts.
  • Keep pi.registerCommand(...) names distinct from bridged tool names unless you intentionally want to shadow/replace that CLI entry point.
  • After adding or changing pi.prompts / pi.skills, reload resources (/reload) or restart the session before testing the slash menu.

Background runtime

Use runtime/ only for long-lived workspace-scoped behavior: startup recovery/reconcile, watchers beyond simple UI sync, subagent orchestration, worktree/PR/cleanup flows, managed dev-server coordination.

Do not use it for simple CRUD, one-shot UI actions, or Pi-CLI-safe logic.

Boundaries:

  • extension/ stays Pi-safe
  • runtime/ is Sero-only; type against @sero-ai/common, never desktop internals
  • Plugin-specific orchestration stays in the plugin, not the host

Host ownership

  • Plugin-specific business logic lives in the plugin, not desktop preload/IPC
  • External plugins consume packages/* via published package names — do not import ../../packages/* source paths
  • Keep plugin-specific types in shared/; only promote neutral cross-plugin contracts to @sero-ai/common
  • Never copy desktop host aliases (@electron, @plugins, @packages, @/) into plugin TS config

State

  • Never use localStorage — all UI state through useAppState (file-backed)
  • Keep state JSON-serialisable (no Date/Map/Set/functions)
  • Always use atomic writes (temp file → fs.rename)
  • Resolve global paths from SERO_HOME / PI_CODING_AGENT_DIR; fall back to ~/.pi only when env vars are unset (Pi CLI mode)

Step-by-step workflow

1. Plan

Decide up front:

  • Plugin name (e.g. myapp) — directory, app ID, state path
  • Display name, Lucide icon, scope (workspace default, or global)
  • State shape shared across surfaces
  • Tools to register
  • Runtime? Only for long-lived orchestration
  • Dev port — unique 5174+; check existing:
    grep -r '"devPort"' plugins/sero-*-plugin/package.json
    

2. Create the directory

plugins/sero-<name>-plugin/
├── package.json
├── vite.config.ts            # UI only, at package root
├── prompts/                  # optional prompt templates (declare in pi.prompts)
├── skills/                   # optional plugin skills (declare in pi.skills)
├── shared/types.ts
├── extension/
│   ├── index.ts
│   └── tsconfig.json
├── runtime/                  # optional
│   ├── index.ts
│   └── tsconfig.json
└── ui/                       # optional
    ├── <Name>App.tsx
    ├── styles.css
    ├── tsconfig.json
    ├── vite-env.d.ts
    └── index.html

Extension-only plugins omit vite.config.ts, ui/, and the sero.app.ui/component/devPort fields.

3. Write package.json

Triple-duty: Pi manifest + Sero app manifest + Sero plugin manifest. See references/templates.md for the full template.

Critical:

  • "keywords": ["pi-package"]
  • Pi SDK packages in peerDependencies
  • @sero-ai/app-runtime and @sero-ai/ui in devDependencies
  • stateFile is required even for global apps (Pi-CLI fallback)
  • ui, component, devPort only when the plugin ships a UI
  • Put extra federated components and host-rendered controls under sero.app.contributes. Never generate legacy widgets, search, explorerView, titlebar, or workspaceCreation fields for a new plugin.
  • runtime only when the plugin ships a runtime; add runtime/tsconfig.json to the typecheck script
  • Declare prompt templates in pi.prompts and plugin skills in pi.skills; folders alone are not auto-loaded
  • If you need a same-name slash shortcut for a bridged tool, use a prompt template in pi.prompts rather than pi.registerCommand with the same name
  • Declare requiredHostCapabilities only for seams you use
  • For extension-only plugins, drop the Vite dev/build scripts
  • For built-in plugins with non-trivial extension deps, set sero.plugin.bundleExtensions: true; list native, large, or runtime-loaded extension packages in sero.plugin.extensionExternals

4. Define shared/types.ts

Single source of truth imported by extension, UI, and runtime.

  • JSON-serialisable only
  • Export a DEFAULT_STATE constant
  • Auto-incrementing IDs for lists

5. Mount the plugin into your workspace (dev only)

Expose the plugin source inside Sero as a multi-root workspace:

sero workspace mount-plugin /absolute/path/to/plugins/sero-<name>-plugin

6. Build the Pi extension

extension/index.ts is standard Pi. Template in references/templates.md.

Key patterns:

  • Use StringEnum from @earendil-works/pi-ai for action enums (not Type.Union)
  • Resolve statePath from ctx.cwd inside execute handlers
  • session_start is a lifecycle event. It fires once in every session kind that loads the extension (chat, subagent, app agent, persistent), with reason startup, resume, fork or reload. Pair anything it starts with session_shutdown, keep it idempotent (a reload ends the old extension copy and starts a new one), and keep it cheap (it runs for every subagent). Pi evaluates the extension module again after a resource reload or when a session opens in another folder, so module-level variables are not shared by every session: keep one-per-process state (a scheduler, a server pool, a shared index) on globalThis under a Symbol.for('<your-package>/<name>') key.
  • Atomic writes only (temp → rename)
  • Keep tool output concise
  • Do not depend on session_switch unless your target SDK guarantees it

7. Optional: background runtime

runtime/index.ts must export createAppRuntime(ctx) and default-export { createAppRuntime }.

Required wiring:

  • sero.app.runtime: "./runtime/index.ts" in package.json
  • runtime/tsconfig.json included in the typecheck script
  • requiredHostCapabilities: ["appRuntime.background"]
  • If the runtime imports native/non-bundle-safe packages, list them in sero.app.runtimeExternals
  • If the extension imports native/non-bundle-safe packages and uses bundled release packaging, list them in sero.plugin.extensionExternals

Context provides: appId, workspaceId, workspacePath, stateFilePath, and host.{appState, subagents, workspace, verification, git, devServers}.

8. Optional: build the UI

React component in ui/<Name>App.tsx. Template in references/templates.md.

Required:

  • Both exports: export function MyApp() AND export default MyApp
  • Import UI components from @sero-ai/ui
  • Import ./styles.css from every exposed MF entry (main app, widgets, etc.)
  • Tailwind semantic colors (bg-background, text-foreground, etc.)
  • ui/styles.css should import @sero-ai/ui/styles/plugin.css and scan plugin-local files with @source "./**/*.{ts,tsx}". The base stylesheet scans only shared primitives and dashboard components; add styles/ai-elements.css, styles/model-selection.css, or styles/context-editor.css when the UI uses those families.
  • UI plugins must set sero.app.styleIsolation to "scope" and add seroPluginCssScope({ pluginId: '<app-id>' }) after Tailwind in vite.config.ts.
  • Do not alias @sero-ai/app-runtime
  • Scope keyboard listeners to the container (tabIndex={0}), never window
  • Use ResizeObserver for dynamic sizing

Also create: vite.config.ts (at package root), ui/styles.css, ui/tsconfig.json, ui/vite-env.d.ts, ui/index.html.

9. Verify

pnpm install
pnpm --filter @sero-ai/plugin-<name> build       # UI only
pnpm --filter @sero-ai/plugin-<name> typecheck
bash scripts/build-plugin.sh plugins/sero-<name>-plugin   # installable bundle / release packaging

All must pass before the plugin is ready.

10. Test end-to-end

cd apps/desktop
SERO_DEV_PLUGINS=<name> bash scripts/dev.sh

Verify:

  1. Plugin appears in the sidebar
  2. UI mutations → state.json → UI re-renders
  3. Agent tool call → state.json → file watcher → UI updates
  4. If the plugin ships pi.prompts / pi.skills, reload resources (/reload) and confirm the slash menu shows them
  5. If tools are CLI-bridged, confirm no command name shadows the bridged entry point (help <tool> / sero <tool> ... still hit the real tool)
  6. If runtime/: starts on workspace open, reacts to state, cleans up on close

Module Federation

  • react / react-dom are shared singletons
  • @sero-ai/app-runtime is not shared via MF — uses globalThis singleton; add to optimizeDeps.exclude
  • Production vite.config.ts must use base: './'
  • MF remote name convention: sero_<id> (valid JS identifier)
  • Exposed module: ./<Component>
  • Every exposed entry must import the plugin stylesheet so external remotes ship their own CSS

Development workflow

  • UI changes (ui/) are handled by the remote Vite dev server. Sero also sends a lightweight cache-busted UI refresh for active local dev sessions; this should remount only the plugin surface, not refresh shell resources or restart agent/runtime state.
  • Changes to extension/, runtime/, shared/types.ts, prompts/, skills/, or package.json refresh the local plugin development session and may reload plugin resources
  • devPort in package.json must match server.port in vite.config.ts
  • Logs: /tmp/sero-vite.log, /tmp/sero-remote-<name>.log, /tmp/sero-electron.log
  • If Local Plugin Development fails before Vite starts with an esbuild host/binary version mismatch or a missing Rollup native package, suspect stale platform-specific optional dependencies in the plugin's own node_modules. Sero attempts a one-time host-side pnpm install --force repair for these failures. If repair still fails, delete node_modules and reinstall from inside that plugin directory on the macOS host before changing source code. Do not run the reinstall from a Sero workspace/container terminal, because that can install Linux native packages into a path that the macOS host dev server later executes.

Conventions

  • Plugin directory: plugins/sero-<name>-plugin/
  • npm package: @sero-ai/plugin-<name>
  • State file: .sero/apps/<id>/state.json (workspace) or under SERO_HOME (global)
  • No source file over 500 LOC — split into sub-components, utils, or types files
  • Always top-level imports; no dynamic await import(...) for source modules

Reference implementations

Plugin Best for
example/sero-notes-plugin/ (in this skill) Canonical minimal plugin. Every surface (tool + CLI bridge, command, runtime, UI hooks, static + dynamic widgets) in the smallest possible footprint.
plugins/sero-git-plugin/ Focused plugin with single tool + substantial UI
plugins/sero-admin-plugin/ Multiple panels, settings, dashboard surfaces
plugins/sero-web-plugin/ Converting an existing Pi extension
plugins/sero-cron-plugin/ Background jobs, command-oriented plugins
../plugins/sero-signal-desk-plugin/ External plugin with background runtime + tool-driven UI + bridged CLI tool

Related skills

  • sero-dashboard-ui (../sero-dashboard-ui/SKILL.md) — building or redesigning dashboard widgets and compact plugin views with the shared @sero-ai/ui dashboard components. Read it whenever a plugin has a widget.

Related docs

  • apps/docs-site/docs/reference/plugin-quickstart.md - plugin setup
  • apps/docs-site/docs/reference/plugins.md - packaging and compatibility
  • apps/docs-site/docs/reference/plugin-extension-points.md - host contributions
  • ARCHITECTURE.md - host boundaries and shared services

Troubleshooting

Problem Fix
Build fails: Could not resolve entry module "node_modules/__mf__virtual/...hostAutoInit..." Keep Vite root at the package root; use build.rollupOptions.input: 'ui/index.html' instead of root: 'ui'
Local Plugin Development fails: Cannot start service: Host version "..." does not match binary version "..." from esbuild The plugin has stale/mismatched native optional dependencies. Sero should try one host-side auto-repair. If it still fails, from the plugin directory on the macOS host (not inside the Sero container), run rm -rf node_modules && pnpm install --force, then retry pnpm exec vite --host 127.0.0.1 or Local Plugin Development. Do not patch Vite config/package versions first.
Local Plugin Development fails: Cannot find module @rollup/rollup-darwin-arm64 or another @rollup/rollup-* native package Same root cause: platform-specific optional dependencies were not installed for the current machine. Sero should try one host-side auto-repair. If it still fails, from the plugin directory on the macOS host, run rm -rf node_modules && pnpm install --force.
App not in sidebar Check sero.app.id/name in package.json, run pnpm install
Agent missing tool Restart dev server, check pi.extensions field
UI changes not showing Check remote Vite running; extension changes need full restart
External plugin missing styles Import ./styles.css from every exposed MF entry; verify @source paths in ui/styles.css
Packaged plugin missing an extension dependency If it must stay in node_modules, add the package name to sero.plugin.extensionExternals; otherwise keep it in dependencies and let bundleExtensions include it in the JS entrypoint
Cannot find module './styles.css' Add ui/vite-env.d.ts with /// <reference types="vite/client" />
Runtime never starts Declare sero.app.runtime + requiredHostCapabilities: ["appRuntime.background"]; check /tmp/sero-electron.log
Build fails because no HTML entry exists Add ui/index.html
"No UI module registered" Set sero.app.component and devPort
Contributed surface does not appear Use a host-defined extensionPoint under sero.app.contributes, expose the named component, and check minSeroVersion
"No workspace selected" Pick one, or set scope: "global"
State not syncing Verify same stateFile path, use atomic writes
Keyboard stealing input Scope listeners to container, not window
lazy: Expected dynamic import Add export default MyApp to component file
MF errors devPort in package.json must match server.port in vite.config.ts

04 同仓库更多 Skills

sero-labs
sero-labs/sero

agent-browser

Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.

测试与质量Skill与智能体开发
★ 28
sero-labs
sero-labs/sero

skill-creator

Guide for writing and improving Sero skills. Use when the user wants to create a new skill, rewrite an existing one, or turn a repeated piece of work into reusable procedural knowledge for the agent.

内容创作者写作与文案Skill与智能体开发
★ 28
sero-labs
sero-labs/sero

frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.

设计师前端开发视觉设计
★ 28
sero-labs
sero-labs/sero

sero-humanize

Use to draft, edit, or audit Sero application content, including UI copy, documentation, README text, and release notes. Use unslop for regular chat and status reports instead. Not for code review or creative and promotional writing.

内容创作者代码审查
★ 28
sero-labs
sero-labs/sero

sero-dashboard-ui

Build, redesign or standardise Sero dashboard widgets and compact plugin views using the shared @sero-ai/ui dashboard components. Use when the user asks to create, restyle, clean up or make consistent a dashboard widget or a data-dense plugin view. Trigger on phrases like "dashboard widget", "widget for the dashboard", "restyle this widget", "make the widget consistent", "compact plugin view", or any work under a plugin's `ui/widgets/`.

设计师数据分析师分析与可视化
★ 28
sero-labs
sero-labs/sero

sero-browser

Use when a user asks to open, browse, capture, screenshot, or record a website/page; mentions browser tabs, visible Browser panel, screen recording, web navigation, website interaction, or browser automation; or when choosing between `sero browser`, `sero app record`, and `automation_browser`.

Skill与智能体开发
★ 28