com um clique
hermes-desktop-plugins
Write desktop app plugins that add UI panes and commands.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Write desktop app plugins that add UI panes and commands.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
Configure, extend, or contribute to Hermes Agent.
Merge ready, green-CI evolution PRs into main and self-update
Drive the user's desktop in the background — clicking, typing, scrolling, dragging — without stealing the cursor, keyboard focus, or switching virtual desktops / Spaces. Cross-platform: macOS, Windows, Linux. Works with any tool-capable model. Load this skill whenever the `computer_use` tool is available.
Analyze the agent's real sessions with users to find what blocks practical task completion, and turn those findings into improvement issues
Audit memory health: secrets, stale, broken wikilinks.
Use when the user wants to do anything in Unreal Engine through Epic's official editor-embedded MCP server (catalog entry: unreal-engine) — build/light/populate scenes, place and transform actors, author Blueprints, animate with Sequencer, create material instances, frame cameras, take screenshots, render, import assets, run PIE test sessions and automation tests, or automate the editor end-to-end from plain-English prompts with no Unreal knowledge required. Covers the tool-search discovery walk (list_toolsets/describe_toolset/call_tool), serial game-thread call discipline, ProgrammaticToolset batching, the Blueprint graph DSL loop, scene-craft numbers (physical light units, exposure, scale conventions), complete build recipes, save/undo hygiene, and extending the tool surface with custom Python toolsets.
| name | hermes-desktop-plugins |
| description | Write desktop app plugins that add UI panes and commands. |
| version | 1.0.0 |
| platforms | ["linux","macos","windows"] |
| metadata | {"hermes":{"tags":["desktop","plugins","ui","extension"],"category":"productivity","related_skills":[]}} |
Write plugins for the Hermes desktop app: statusbar items, layout panes,
command-palette commands, keybinds, routes, and themes. A plugin is a single
plain-JavaScript ESM file the app loads at runtime — no build step, no repo
changes. A plugin can also talk to its own Python backend namespace
(ctx.rest/ctx.socket → /api/plugins/<id>); the general Python plugin
system (~/.hermes/plugins/) is otherwise documented separately.
Full human reference (every export, area payloads, backend, security):
website/docs/developer-guide/desktop-plugin-sdk.md.
$HERMES_HOME/desktop-plugins/ (usually
~/.hermes/desktop-plugins/).$HERMES_HOME/desktop-plugins/<name>/plugin.js from
templates/plugin.js (relative to this skill directory) — that's
~/.hermes/... by default, or ~/.hermes/profiles/<profile>/... under a
named profile. Keep <name> equal to the plugin id.The ONLY import surface is @hermes/plugin-sdk (plus react /
react/jsx-runtime, which resolve to the app's own React — write UI with
jsx() calls, not JSX syntax; the file is not compiled).
host.state.* — readonly reactive atoms: activeSessionId, cwd,
gateway, model, profile, viewport. Read with .get() in handlers,
useValue(atom) in components.host.request(method, params) — gateway JSON-RPC (sessions, config,
skills, cron — everything the app uses).host.onEvent(type, fn) — live gateway events ('*' for all). Returns a
disposer.host.notify({ kind, message }), host.navigate(path), host.logs(...),
host.status(), haptic('tap').ctx.register({ id, area, order?, render?, data? }) — contribute UI.
Key areas: 'statusBar.right'/'statusBar.left' (chips),
'panes' (layout zones — set title and
data: { placement, dock?, width?, height? }; the pane auto-joins a
matching zone), PALETTE_AREA (⌘K commands), KEYBINDS_AREA (rebindable
actions).placement: 'left'|'right'|'bottom'|'main' is the
semantic role — the pane stacks (tabs) with existing panes of that role.
To land on a specific EDGE instead, add dock: { pane, pos } — the same
gesture as dragging onto a pane's drop chip. pane is any pane id
(workspace is the main thread; also sessions, terminal, files,
review, logs), pos is 'top'|'bottom'|'left'|'right'|'center'.
E.g. "below the conversation" = dock: { pane: 'workspace', pos: 'bottom' }
— declare a height (e.g. '200px') so it doesn't take half the zone.area: ROUTES_AREA with data: { path: '/my-page' }
and a render — the page mounts in the workspace (main) pane like any
built-in view. Make it reachable with a sidebar nav row:
ctx.register({ id: 'nav', area: SIDEBAR_NAV_AREA, data: { path: '/my-page', label: 'My Page', codicon: 'project' } })
(renders below Artifacts, lights up at the route) — and/or a
PALETTE_AREA command calling host.navigate('/my-page').ctx.storage.get/set/remove — persistence namespaced to your plugin.ctx.i18n.register({ en, ja, ... }) — ship your OWN locale bundles, scoped
to your plugin (never edit core en.ts). Values are literal strings or
interpolator functions; nested trees are addressed by dot-path. Read them
reactively in components with usePluginI18n(id) returning t('key', ...args)
(re-renders on a locale switch), or via ctx.i18n.t in handlers/stores.
Resolution follows the app's active locale, then your en, then the raw key.useQuery/useMutation/useQueryClient/queryClient (the app's ONE
React Query client — cache, dedupe, refetchInterval, invalidate like core;
never hand-roll a poll loop), plus atom/computed for plugin-local state.plugin_api.py (under
~/.hermes/plugins/<id>/dashboard/, manifest "api": "plugin_api.py"), reach
it with ctx.rest('/path', { method?, body?, timeoutMs? }) and its live twin
ctx.socket('/events', onMessage) — both scoped to /api/plugins/<id> by
construction (traversal rejected). ctx.socket is a no-op on OAuth
remotes, so always keep a polling fallback. The Python backend is imported
only when the plugin is in plugins.enabled in config.yaml (separate from
the in-app enable toggle). For gateway-wide data use host.request /
host.onEvent instead.Contribute (mount-scoped): render jsx(Contribute, { area, id, children })
inside a component so page-owned chrome (e.g. a titlebar control in
TITLEBAR_AREAS.center) leaves when the page unmounts — ctx.register is for
permanent contributions.defaultEnabled: false on the default export ships an opt-in plugin: it
inventories in Settings → Plugins, off until the user flips it on.Button, Input,
Textarea, Select*, Switch, Checkbox, SegmentedControl, Tabs*,
Dialog*, ConfirmDialog, DropdownMenu*, ContextMenu*, Popover*,
Tip/Tooltip*, Badge, Kbd/KbdGroup, SearchField, ScrollArea,
Separator, Skeleton, GlyphSpinner, EmptyState, ErrorState,
CopyButton, StatusDot, LogView, Codicon, DecodeText, plus cn
and icons.*. Prefer these over hand-rolled elements so the plugin looks
native; style with theme vars, never hardcoded colors.id; the folder name must match.templates/plugin.js; keep the default export shape
({ id, name, register(ctx) }).area: 'panes' with a placement hint and a
render returning your component — the app places it into a sensible
zone automatically; the user can drag it anywhere afterwards.host.request and/or subscribe with host.onEvent;
never poll faster than a few seconds.#000, black, rgb(...)). Panes
already sit on the app's editor background — leave the background alone
and use theme variables for everything else: var(--ui-text-secondary),
var(--ui-text-quaternary), var(--ui-stroke-secondary),
var(--ui-accent). For canvas drawing, resolve them once with
getComputedStyle(canvas).getPropertyValue('--ui-accent').StatusDot) is a ReferenceError at render. Double-check every
identifier in your jsx() calls appears in the import line.ResizeObserver and
re-size the canvas (width/height attributes, not just CSS) — panes resize
constantly (sash drags, layout switches); a mount-time-only size leaves
blank space or blurry scaling.jsx('div', { children: ... }) from react/jsx-runtime.@hermes/plugin-sdk, react, and
react/jsx-runtime; other specifiers fail to resolve.$atom.get()), never from render
closures — rapid events will otherwise see stale values.useValue) only in the leaf that
renders the value.