hermes-desktop-plugins
Skill0 starsChanged 35 days agoMITView on GitHub
Write desktop app plugins that add UI panes and commands.
9 other projects use a version of this, 6 of them edited.
Adopted by
Most recently taken up by
- delorenj/skillsedited, first seen Nov 7, 2026
- AtlasOmnia/hermes-custom-packedited, first seen Oct 13, 2026
- pedroiff0/awesome-skillsedited, first seen Sep 29, 2026
- zouzhenglu/hermes-skillsword for word, first seen Aug 27, 2026
- 2867203296/hermes-agentword for word, first seen Aug 19, 2026
- Samin12/skillsedited, first seen Aug 14, 2026
Used with
Projects with it also use hermes-themes.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
AtlasOmniaSkillhermes-themes
hermes-themes — Author a Hermes color theme that skins every surface.
Also in 3 projects that use this
Other versions9 projects use a version of this.
Word for word · 3
- zouzhenglu/hermes-skills★ 1100% same
- 2867203296/hermes-agent★ 0100% same
- jamesbmour/Hermes-Skills★ 0100% same
Edited · 6
- AtlasOmnia/donna-starter★ 13699% same
- AtlasOmnia/hermes-custom-pack★ 7599% same
- delorenj/skills★ 1798% same
- Samin12/skills★ 896% same
- pedroiff0/awesome-skills★ 197% same
- lucadominguez/hermes-skills★ 098% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository16 other files it gives its agents.
- brandkitSkill
- industrial-brutalist-uiSkill
- gpt-tasteSkill
- imagegen-frontend-mobileSkill
- imagegen-frontend-webSkill
- image-to-codeSkill
- minimalist-uiSkill
- nft-drop-launchSkill
- full-output-enforcementSkill
- redesign-existing-projectsSkill
- high-end-visual-designSkill
- stitch-design-tasteSkill
- design-taste-frontendSkill
- design-taste-frontend-v1Skill
- wallet-cluster-reconSkill
- yuanbaoSkill
The file
We split this skill into the rules it gives an agent
9 rules. 4 are common, 0 are distinctive, and 5 are very specific to this project.
4
Common
Most of the 6 edited versions we read keep these.
0
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Common: line 15
Write plugins for the Hermes desktop app: statusbar items, layout panes, command-palette commands, keybinds, routes, and themes.
- Very specific: line 34
Write access to $HERMES_HOME/desktop-plugins/ (usually ~/.hermes/desktop-plugins/).
- Common: line 47
If loading fails the app shows a toast naming the error — fix the file and save again.
- Very specific: line 125
Start from templates/plugin.js.
- Common: line 132
Write the file with your file tools, then ask the user to run
- Very specific: line 137
NEVER hardcode colors or backgrounds (#000, black, rgb(...)).
- Very specific: line 152
Do not import anything except @hermes/plugin-sdk, react, and react/jsx-runtime.
- Very specific: line 156
Keep components small; subscribe (useValue) only in the leaf that renders the value.
- Common: line 162
No error toast ("Plugin <name> failed to load") appears.
“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Hermes Desktop Plugins Skill
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.
When to Use
- The user asks for a new desktop UI element (a pane, a statusbar widget, a dashboard, a command) without modifying the app itself.
- You want to surface data you compute (via gateway RPC) inside the app.
Prerequisites
- The Hermes desktop app (it loads plugins; the CLI/gateway alone does not).
- Write access to
$HERMES_HOME/desktop-plugins/(usually~/.hermes/desktop-plugins/).
How to Run
- Create
$HERMES_HOME/desktop-plugins/<name>/plugin.jsfromtemplates/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 pluginid. - The desktop app watches that directory: the plugin loads within a few seconds of the file landing, and every later save hot-reloads it in place. No reload step. (Fallback if it doesn't appear: ⌘K → Reload desktop plugins.)
- If loading fails the app shows a toast naming the error — fix the file and save again.
Quick Reference
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 — settitleanddata: { placement, dock?, width?, height? }; the pane auto-joins a matching zone),PALETTE_AREA(⌘K commands),KEYBINDS_AREA(rebindable actions).- Pane placement:
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, adddock: { pane, pos }— the same gesture as dragging onto a pane's drop chip.paneis any pane id (workspaceis the main thread; alsosessions,terminal,files,review,logs),posis'top'|'bottom'|'left'|'right'|'center'. E.g. "below the conversation" =dock: { pane: 'workspace', pos: 'bottom' }— declare aheight(e.g.'200px') so it doesn't take half the zone. - Full PAGES: register
area: ROUTES_AREAwithdata: { path: '/my-page' }and arender— 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 aPALETTE_AREAcommand callinghost.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 coreen.ts). Values are literal strings or interpolator functions; nested trees are addressed by dot-path. Read them reactively in components withusePluginI18n(id)returningt('key', ...args)(re-renders on a locale switch), or viactx.i18n.tin handlers/stores. Resolution follows the app's active locale, then youren, then the raw key.- Data:
useQuery/useMutation/useQueryClient/queryClient(the app's ONE React Query client — cache, dedupe,refetchInterval, invalidate like core; never hand-roll a poll loop), plusatom/computedfor plugin-local state. - Backend: if the plugin ships a Python
plugin_api.py(under~/.hermes/plugins/<id>/dashboard/, manifest"api": "plugin_api.py"), reach it withctx.rest('/path', { method?, body?, timeoutMs? })and its live twinctx.socket('/events', onMessage)— both scoped to/api/plugins/<id>by construction (traversal rejected).ctx.socketis a no-op on OAuth remotes, so always keep a polling fallback. The Python backend is imported only when the plugin is inplugins.enabledinconfig.yaml(separate from the in-app enable toggle). For gateway-wide data usehost.request/host.onEventinstead. Contribute(mount-scoped): renderjsx(Contribute, { area, id, children })inside a component so page-owned chrome (e.g. a titlebar control inTITLEBAR_AREAS.center) leaves when the page unmounts —ctx.registeris for permanent contributions.defaultEnabled: falseon the default export ships an opt-in plugin: it inventories in Settings → Plugins, off until the user flips it on.- Users manage plugins in Settings → Plugins (enable/disable live, reveal folder). A disabled plugin stays disabled across restarts — don't fight it; the user turned you off.
- UI: the app's design language, importable directly —
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, pluscnandicons.*. Prefer these over hand-rolled elements so the plugin looks native; style with theme vars, never hardcoded colors.
Procedure
- Pick a short kebab-case
id; the folder name must match. - Start from
templates/plugin.js; keep the default export shape ({ id, name, register(ctx) }). - For a pane, register
area: 'panes'with aplacementhint and arenderreturning your component — the app places it into a sensible zone automatically; the user can drag it anywhere afterwards. - Fetch data with
host.requestand/or subscribe withhost.onEvent; never poll faster than a few seconds. - Write the file with your file tools, then ask the user to run Reload desktop plugins from ⌘K.
Pitfalls
- NEVER hardcode colors or backgrounds (
#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 withgetComputedStyle(canvas).getPropertyValue('--ui-accent'). - Reference only what you imported — a component you forgot to import
(e.g.
StatusDot) is a ReferenceError at render. Double-check every identifier in yourjsx()calls appears in the import line. - Canvas panes MUST track their container with a
ResizeObserverand 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 syntax will not parse — the file loads uncompiled. Use
jsx('div', { children: ... })fromreact/jsx-runtime. - Do not import anything except
@hermes/plugin-sdk,react, andreact/jsx-runtime; other specifiers fail to resolve. - Handlers must read state imperatively (
$atom.get()), never from render closures — rapid events will otherwise see stale values. - Keep components small; subscribe (
useValue) only in the leaf that renders the value.
Verification
- The plugin's UI appears after Reload desktop plugins.
- No error toast ("Plugin
<name>failed to load") appears; if it does, the message names the failure — fix and reload. - For panes: the new zone is visible and draggable like any core pane.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

