computer-use skill
Drive the desktop background-first; escalate on signal.
Is the computer-use skill safe?
Read the findings before you install it. We read 1 file in the folder on 2026-11-04.
- high
SKILL.md:308Downloads a script and runs it in one step, so what runs is whatever that server sends that day. Common for installers, and still worth a look at the address.
| `blocked pattern in type text` | You tried to `type` a shell command matching the dangerous-pattern block list (`curl ... \| bash`, `sudo rm -rf`, etc.). Break the command up or reconsider |
Install the computer-use skill
A skill is a folder. Copy it into your agent's skills folder and the agent loads it when the task matches its description.
git clone --depth 1 https://github.com/NousResearch/hermes-agent.git /tmp/hermes-agent mkdir -p ~/.claude/skills cp -r /tmp/hermes-agent/skills/autonomous-ai-agents/computer-use ~/.claude/skills/computer-use
In the Claude apps, zip the folder and upload it from the Skills settings. The folder on GitHub
The instructions your agent would load
SKILL.md as published, without the frontmatter. Read it on GitHub
Computer Use (universal, any-model, cross-platform)
You have a computeruse tool that drives the user's desktop in the background** — your actions do NOT move the user's cursor, steal keyboard focus, or switch virtual desktops / Spaces. The user can keep typing in their editor while you click around in a browser in another window. This is the opposite of pyautogui-style automation.
Everything here works with any tool-capable model — Claude, GPT, Gemini, or an open model on a local OpenAI-compatible endpoint. There is no Anthropic-native schema to learn.
Hermes drives cua-driver under the hood. This skill teaches the Hermes computeruse action vocabulary, which is NOT the driver's raw MCP vocabulary. Call the actions documented below and never the driver's tools by name: capture is a Hermes action that maps to the driver's getwindowstate; element=N is a Hermes argument that the wrapper translates into the driver's elementtoken handle. If you see a driver-side error mentioning snapshotid, elementtoken, or "no reviewed risk classification", you (or a stale description) called the raw driver vocabulary — go back to the actions below.
The canonical workflow
Step 1 — Capture first. Almost every task starts with:
computer_use(action="capture", mode="som", app="<the app you're driving>")Returns a screenshot plus an indexed element list like:
#1 AXButton 'Back' @ (12, 80, 28, 28) [Chrome]
#2 AXTextField 'Address bar' @ (80, 80, 900, 32) [Chrome]
#7 Link 'Sign In' @ (900, 420, 80, 24) [Chrome]
...The #N index is the ONLY element handle you use. Behind it the wrapper keeps this snapshot's opaque per-element token and sends it with every element=N action, so a click on an index from a superseded snapshot is refused explicitly (stale) instead of landing on the wrong control. Re-capture after anything that changes the screen; indices do not survive it.
The role names match the host platform's accessibility framework (AXButton on macOS, Button on Windows UIA, push button on Linux AT-SPI) — treat them as labels, not as strict types.
Step 2 — Click by element index. This is the single most important habit:
computer_use(action="click", element=7)Much more reliable than pixel coordinates for every model. Claude was trained on both; other models are often only reliable with indices.
Step 3 — Verify. After any state-changing action, re-capture. You can save a round-trip by asking for the post-action capture inline:
computer_use(action="click", element=7, capture_after=True)Capture modes
Current drivers always return the screenshot AND the tree in one call; mode decides what Hermes hands back to you, not what the driver does. There is no numbered overlay burned into the screenshot — the index list is the map; ground on both and cross-check (the tree lies on some surfaces).
No vision model? If your main model can't read images (or the provider rejects image tool results), Hermes routes the screenshot through the auxiliary vision model and you get a text description instead of pixels. Configure auxiliary.vision in config.yaml to pick that model, or use mode="ax" and drive by element index without a screenshot at all.
Actions
capture mode=som|vision|ax app=… (default: current app)
click element=N OR coordinate=[x, y] button=left|right|middle
double_click element=N OR coordinate=[x, y]
right_click element=N OR coordinate=[x, y]
middle_click element=N OR coordinate=[x, y]
drag from_element=N, to_element=M (or from/to_coordinate)
scroll direction=up|down|left|right amount=3 (ticks)
type text="…"
key keys="<save shortcut>" | "return" | "escape" | "<modifier>+t"
set_value element=N value="…" (selects/sliders without opening the menu)
wait seconds=0.5
list_apps
list_windows
focus_app app="<app name>" raise_window=false (default: don't raise)All actions accept optional capture_after=True to get a follow-up screenshot in the same tool call. All actions that target an element accept modifiers=[…] for held keys.
The input actions (click, doubleclick, rightclick, middleclick, drag, scroll, type, key) also accept deliverymode. The optional bringtofront=True request invokes a separately approved standalone focus tool before foreground input; it is never an input-action property.
The verify → escalate ladder (background-first)
cua-driver delivers input in the background by default (no focus steal), but that is the first rung, not the only one. Every input action returns a structured verdict; read it and climb only when the driver tells you to.
Returned fields (present when the driver supports them):
(delivered, but confirm it yourself by re-capturing), or "suspected_noop" (ran but almost certainly did nothing).
- effect: "confirmed" (driver read the result back — done), "unverifiable"
only when there's a next rung to try.
- escalation: {recommended: "px" | "foreground", reason} — present
"foreground_unsupported", or "stale" (re-capture, then retry by index).
- code: a structured refusal like "background_unavailable",
- verified: true only on AX read-back.
Walk it in order:
you're done.
- Element, background (default). click(element=N). If effect:"confirmed",
capture/state before any retry. Do this even when escalation.recommended is present; it is advisory, not proof that successful input should repeat.
- Fresh verification. effect:"unverifiable" means inspect a fresh
refusal recommends "px" (or a degraded capture has no elements), click by coordinate=[x,y] instead of element.
- Pixel, background. After effect:"suspected_noop" or a structured
code:"backgroundunavailable", or a verified pixel no-op, re-issue the SAME action with deliverymode="foreground". This briefly raises the window and restores focus after; pair with bringtofront=True for a short sequence to avoid per-call flashes. It needs its own approval (it's a visible focus change) and is only appropriate when the user isn't actively working. Classic cases: Electron/Chromium consent dialogs (e.g. tldraw offline's "Run Script"), DirectInput games, raw-input canvases.
- Foreground. After effect:"suspected_noop",
Some Qt text components (KTextEditor: Kate, KWrite, KDevelop) discard SYNTHETIC X keystrokes entirely — foreground type reports ok ("Typed N characters into the focused widget", effect:"unverifiable") but a fresh AX capture shows the text never arrived, and raw XTest fails identically (proven live, Aug 2026 — it is the toolkit, not the driver; the same foreground route works on kcalc/Chrome). After ONE such verified-lost round trip, stop retrying input rungs: write the file with terminal/file tools and let the editor reload it, or drive the app's DBus/CLI interface. Never loop the ladder against a surface that verifiably swallows synthetic input.
- Keystrokes verified-lost on a KDE/Qt editor → use the app's own I/O.
computer_use(action="click", element=7)
# → {effect: "suspected_noop", escalation: {recommended: "foreground", ...}}
computer_use(action="click", element=7, delivery_mode="foreground")
# → {effect: "unverifiable", path: "x11_pixel_fg"} then re-capture to confirmEscalate to foreground as a REACTION to a returned signal, never as a prediction from the app being Electron/Chromium/GTK. A confirmed effect is done and must not be duplicated. Different controls in the same app behave differently. Do NOT silently retry the same rung, and do NOT conclude "cua-driver can't drive this app" — climb the ladder. If deliverymode="foreground" returns code:"foregroundunsupported", the live action schema lacks that property; choose another verified rung without inferring support from the executable's reported version.
Page content is a separate toolset
computeruse is desktop-only: it does not expose a typed route for browser page content (no cuabrowser actions). For reading or acting on a page's DOM — navigation, clicking a link by text, typed input into a form field — use the separate browserexec tool (Python CDP helpers driving a headless Chromium); its own schema documents the current contract. Reserve computeruse for browser chrome* (the address bar, permission prompts, extension popups, native dialogs) and anything else on screen that isn't page content.
Key shortcuts vary per platform
Use the host's idiomatic modifier:
When in doubt, capture and look for menu hints, or ask the user which shortcut to use.
Background rules (the whole point)
to bring a window to front. Input routing works without raising.
- Never raisewindow=True** unless the user explicitly asked you
elements, doesn't leak other windows the user has open.
- Scope captures to an app (app="Chrome") — less noisy, fewer
elements on any virtual desktop / Space regardless of which one is visible.
- Don't switch virtual desktops / Spaces. cua-driver drives
another window. Don't grab focus. Don't pop modals to the front.
- The user can be on the same machine. They might be typing in
More skills from NousResearch/hermes-agent
- A1passwordSet up op CLI, sign in, and read or inject secrets.
- A3-statement-modelBuild integrated IS/BS/CF financial workbooks in Excel.
- AaccelerateRun PyTorch training across GPUs with minimal changes.
- Cactual-setupSet up Actual Computer (actual.inc) inference in Hermes.
- Aadversarial-ux-testRoleplay a hostile user to find and triage UX pain points.
- Aagent-merge-conflict-arbiterNeutral arbiter for merge conflicts between two agents.
- AagentmailUse when an agent needs AgentMail CLI email inboxes.
- Aai-presenter-videoMake a verified AI presenter video from script + image.
- AairtableAirtable REST API via curl. Records CRUD, filters, upserts.
- AanatomyExplain an idea as an interactive isometric machine figure.
- Cantigravity-cliOperate the Antigravity CLI (agy): plugins, auth, sandbox.
- Aapple-notesManage Apple Notes via memo CLI: create, search, edit.