unix_computer_use
Skill897 starsChanged 4 months agoMITArchived repositoryView on GitHub
Local and remote desktop observation and input, with macOS semantic actions, explicit foreground control, screenshot-bound coordinates and honest outcome receipts.
- Reads credentials
2 other projects use a version of this.
Adopted by
Steady: 0 new projects took it in the last 4 weeks, none the 4 weeks before.thin data
Most recently taken up by
- razzant/ouroborosword for word, first seen Jul 24, 2026
Other versions2 projects use a version of this.
Word for word · 2
- razzant/ouroboros★ 1.7k100% same
- joi-lab/ouroboros-desktop★ 209100% same
Edited · 0
None found.
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 repository11 other files it gives its agents.
- docs/llms.txtllms.txt
- Browser speechSkill
- Device voiceSkill
- gemini_liveSkill
- grok_realtimeSkill
- live_conversationSkill
- openai_realtimeSkill
- telegramSkill
- TemplatesSkill
- ui_designSkill
- windows_computer_useSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 100
Prefer an application's own scripting commands when they express the task.
- Very specific: line 156
Wait for the owner's return and obtain a fresh observation before continuing.
- Very specific: line 486
Default active connection is local.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Unix Computer Use
This bundled extension exposes a reviewable computer-use substrate for local
macOS/Linux desktops and explicitly configured remote targets. All actions are
for supervised, low-risk workflows under human or benchmark observation. It is
offered and on by default on macOS and Linux (os); Windows desktops use
windows_computer_use. A previously enabled Unix remote skill stays available
on Windows through later OFF/ON. Actor-less legacy ON is retained too: manual
and early automatic choices cannot be distinguished. This adds no local Windows backend.
Tools:
capabilitiesreports the platform, display-session type, who is in front (local and remote), the OS grants the local process holds (prompt-free preflights on macOS: Accessibility, Screen Recording, input events;permission_state_verifiedis true only when they ran; a remote machine's grants are not probed), whetherapp=targeting works in this session and what it is resolved from, and the detected screenshot/input backends plus the active connection.list_connections,add_connection,test_connection,activate_connection, anduse_localmanage the local/remote connection registry in skill state.screenshotcaptures the main display (or, on macOS withapp=, the display holding that application's front window), downscales the image to fit WXGA (1280x800, configurable). When geometry and persistence succeed, it returnsmapped_input_available: truewith a saved image-to-input transform and observation ID. Otherwise the image remains usable for viewing, butmapped_input_available: falsemeans nocoord_transformorobservation_idis supplied. SSH macOS uses the primary display capture and its matching CoreGraphics bounds; it does not map a single-screen image against the Finder desktop union.display.selected_bysays how the display was chosen:default,app, orfallbackwhen the named application was resolved but its window could not be located (app_window_unreadsays why) and the main display was captured instead; the result then carries noapp. Once the display list is read, anapp=not resolved to one running application (not running, ambiguous, or unknown because the helper gave no answer) is refused: nothing is captured. If the display list cannot be read, the application is not looked up and no app selection is established: the default capture stays capture-only (no mapping), withselected_by: default,app_display_unreadsaying why and noapp. Linux and the remotes capture their one screen and ignoreapp=(the result says so). Pass coordinates read off the returned image together with itsobservation_idto the input tools; mapping is bound to that captured observation. The result carries the typedauto_attach_imagefield, so the host attaches the image to the conversation in the same round; an engine that cannot take images receives the path instead (host behaviour), so the text facts in every other tool are the way to check without a picture.click(left/right/middle, double/triple), the thin aliasesdouble_click/triple_click(models call these names unprompted),move,left_click_drag,mouse_down/mouse_up,cursor_position,type_text,key,hold_key,scroll, andwaitexecute input through platform tools when available. Pointer coordinates pass through one normalizer that accepts the malformations models actually emit (the pair packed intoxwithyabsent or duplicated) and fails loudly on anything ambiguous.activate_appbrings a running application to the front (name, bundle id / WM_CLASS, orpid:<n>aswindow_listprints it; resolved from the running list, never launched) and reports who is in front afterwards.window_listlists running applications with their windows: who is in front, titles, bounds, pid, and on macOS whether the application has its own scripting commands (scriptable);app=filters,limitcaps the rows.ax_treereads one window as text through the Accessibility API, bounded: the frontmost application's window, orapp='s without bringing it forward;window=(a window ID or exact title) reads that window instead.element_actperforms local macOSpressor textset_valuethrough the control's own Accessibility interface. It never activates, focuses, injects global input, writes the clipboard or falls back to pointer/keyboard input. The application itself may still raise a window: foreground observations are evidence, not a universal non-interference guarantee.remote_execruns a shell command on the active remote backend only; it refuses on local backend. Use ordinary Ouroboros shell/file tools for local work.
Choosing a control path
Prefer an application's own scripting commands when they express the task. For
background UI work, use element_act(app=..., do="press"|"set_value",
select={identifier, role, label, nth}, window=..., text=...): name only the
selector fields needed, from a fresh ax_tree/window_list. window is a
window ID or exact title; identifier is AXIdentifier, label the full exact
title/description, and nth is one-based among current matching controls.
Tree IDs and coordinate marks are not reusable element handles. One helper
process resolves the application, finds and revalidates the element, acts and
reads it back; each call pays one helper startup and a bounded walk. Ambiguous
or incomplete selection refuses. timeout_ms is ONE budget from the start of
the helper's element operation (after its interpreter has started; the
application's resolution included), with or without observe: the search gets what is
left, and an element found and checked after it is spent is not acted on
(budget_exhausted, dispatched: false). The settle and readback after an
action take only what remains; a readback with nothing left is skipped and says
so, and the action's receipt stands. The budget is checked between steps, not a
wall-clock cap: a step already running finishes under its own bound (an
Accessibility call up to 1 s, an lsappinfo read up to 8 s), so a call can
outlast timeout_ms; the hard stop is the helper's process limit (about
timeout_ms + 10 s).
observe=true adds observation: the same window element read again after the
one action (or after a refusal, read-only), using ax_tree's node shape and
text_limit; window is an object containing its title and available window ID.
The tree is bounded by this call's max_nodes (default 600; ax_tree: 150). It spends the same timeout_ms: the read gets what is left,
the window's facts included (budget_exhausted when nothing is). The
receipt is printed once complete (after the readback and front_after) and
before the read starts, so a read that fails, runs out or is
cut off by the helper's timeout (helper_stopped) leaves the action's receipt
as it was: the top-level outcome is always the action's. A helper stopped
before that line, with or without observe, leaves no receipt: the outcome is
unknown (the action may have run), as below. A call carries one
action at most. Several independent calls in one response are admitted one by
one; a step that depends on an earlier step's outcome belongs to the next round.
On local macOS, ax_tree and element_act first request app-local accessibility
exposure with Electron's documented AXManualAccessibility=true attribute. The
one attempt runs under the AX messaging timeout before window selection, and
spends the action's existing time budget. ax_exposure records its status and
AX error code: accepted confirms the request only, unsupported is ordinary
for other applications, unknown means a timeout, and failed retains other
errors. Reads and actions still use their normal selection and verification;
there is no retry, readiness assumption, activation or input fallback. This
changes the target's accessibility exposure, not macOS accessibility settings.
See Electron's client-side documentation.
The receipt separates dispatched, action_outcome and element_after from
front_before/front_after. A completed AX call is not proof of the goal;
verify the application's result. CannotComplete, a missing helper answer or
a timeout can follow an effect: re-observe, never blindly replay. A failed
readback does not erase an already completed action. Unsupported controls
require a deliberate choice of another method, never a hidden foreground fallback.
For foreground work, announce the handoff before activation. After the one-shot
activate_app, use targeted inputs with raise_policy="never": switching to
another app then makes the next call refuse instead of taking the front back.
Wait for the owner's return and obtain a fresh observation before continuing.
Omitted policy remains allow for existing explicitly requested foreground
work. Untargeted input is global and belongs to the same announced handoff,
not background collaboration. Existing task Pause/Stop prevents later tool
starts; it cannot retract an admitted OS action. Front checks are not an
exclusive desktop lease, nor a guarantee against a switch during dispatch.
Acting in a named application (app=):
click,double_click,triple_click,left_click_drag,mouse_down,type_text,key,hold_keyandscrolltake an optionalapp: a running application's name (case-insensitive), its bundle id or WM_CLASS, orpid:<n>copied fromwindow_list; on X11 and OSWorld also a window's exact title or its0x…id. Resolution is exact: no prefix or substring guessing (on X11 and OSWorld a substring hit is returned as a candidate, so the exact name can be given; macOS and SSH answer with the running list; the one looser form is the WM_CLASS rule in the X11 row below, wherewmctrljoins instance and class with a dot). On X11 and OSWorld the target is a set of windows: the one window for a title or id, every window of that WM_CLASS for a class (a process may host several classes, as LibreOffice's Writer and Calc do), every window of the process forpid:; "in front" means the active window is one of them. The tool resolves the name against the running list without launching anything, withraise_policy="allow"brings it forward if needed, then verifies the front before input.neverrefuses instead of activating and attempts no switch (target_lost, orfront_unreadwhen the front is unread). Two matches are a refusal that lists the candidates; an application that is not running is a refusal that lists the running applications; one that did not come to the front (macOS 14+ may refuse activation from a background process) is a refusal that names who is in front; a front that could not be read after the switch is the refusalfront_unread. A refusal sends nothing and carries no effect mark (a switch it observed first keeps its own,activation_done). An emptyapp(what a voice model sends for an unused optional field) means "untargeted": the input goes to whatever is in front, as before.- A
keysequence ('down down return') validates every step before input or activation, applies the raise policy once, and rechecks the front before every later step: when the named application (or window) has left the front (acmd+tabin the sequence, the owner switching away, a sibling window of the same process coming forward for a title-named target, a window of the class that was not listed with a class-named target, such as a dialog a step opened) the remaining steps are not sent (target_lost,steps_donesays how many ran); a switch that ran before a step that could not be sent keeps its mark (activation_done). Withoutapp=every step goes to whatever is in front at that moment, and the receipt names each step's recipient when they differ (steps_delivered_to,"unknown"for a step whose front could not be read). On a sequence that stopped,dispatchedis the fact for the step that failed (steps_donecounts the steps that ran before it; a step whose remote call did not answer isnull, and the note says so, never "not sent"). A tool's registered budget covers one input's worst case (type_text180 s, the other inputs 90 s,moveandmouse_up60 s), not a long sequence: when the host's budget ends a call, no receipt comes back. moveandmouse_upnever change which application is in front: a drag must end where it started.- Keyboard focus follows the switch and stays there, as if the owner had clicked the application; nothing moves it back. Do this only when the owner asked for an action in that application (owner decision 27.10.2026).
- Where an application has its own commands (macOS:
scriptable: trueinwindow_list; AppleScript/JXA throughrun_command/run_script, e.g.osascript -e 'tell application id "com.apple.iWork.Keynote" to show next'while a presentation plays, orset current slide of front document to slide 2in the editor), prefer them: they do not depend on which window is in front and they can read the state back (slide number of current slide). A keystroke is the right instrument only where no such command exists, and the key that means "next" differs per application and per mode (Keynote advances on Right Arrow while presenting, Down Arrow in the editor's navigator).
Coordinate contract:
- Mapped input requires the screenshot's
observation_idand coordinates in its image space. A missing, expired or mismatched mapping refuses before activation/input; there is no implicit raw-coordinate fallback. The binding covers the connection, resolved destination, backend and observed geometry, not unchanged page content: obtain a fresh observation after UI changes.raw=trueexplicitly selects measured native input coordinates and bypasses remapping. A coordinate-freemouse_upremains usable to release a hold. - macOS input consumes LOGICAL points while screenshots are physical pixels;
the stored transform folds the Retina scale in, computed from the captured
display's own CoreGraphics bounds (a second display no longer stretches the
scale), including a negative origin for a display left of or above the main
one (cliclick then receives
=-prefixed absolute coordinates, locally and on a Mac over SSH; on a Mac negative raw coordinates are valid input, while an X11/Wayland desktop and an OSWorld guest, which have no screen space left of or above their origin, refuse them before any call, also when a stored transform maps a point there: a screenshot of another display or backend, so take a screenshot of this desktop first). Missing exact display bounds yield a capture-only result, not a guessed Finder-union mapping. SSH Mac capture and mapping use the same primary display; unavailable geometry also means capture-only. Raw coordinates require independently verified native points.
Input receipts (the same contract on every backend):
- A successful input call returns
observed_effect: true: the input command ran and reported success. It proves dispatch, not that the screen changed or that the task's goal was met; the host's task result review reads it as work the task produced. - Beside it an input receipt carries the place, each field where it is known
(a refusal carries
target,reasonand the front it read, nodispatched: nothing ran):delivered_to(the application or window in front when the input was dispatched: app, pid, bundle id or class, window id and title where the backend knows them;"unknown"when no reader answered),dispatched(whether the input helper ran at all:falsefor a refused combo or a failed clipboard write,truefor a helper that ran, whatever it reported,nullwithtransport_failedwhen a remote call did not answer, so whether it ran is unknown),front_before(only when the tool switched applications first),front_after(who was in front when the tool read again after the input: present when that is another place thandelivered_to(another window where the backend knows windows, else another application) or whendelivered_tois unknown; an observation, not proof that the input caused a change),target(withapp=: what was requested and resolved,verifiedin front before dispatch,activatedwhen the front was read as another place before and as the target afterwards,already_front,method,waited_ms(on a remote: the host's round trip for the switch);activatedisnullwhen the previous front could not be read),helper_stderrwhen the input helper printed anything, and onenotesaying what this tool observed and did not observe (a dispatched input: no UI change; a withheld one: "not sent"; a lost remote answer: unknown). - An input whose helper did not report success (non-zero exit, timeout) is
ok: falsewithout the input's mark; it carriesfront_at_dispatchinstead ofdelivered_to, because whether anything reached the screen is unknown. An input that was never dispatched (a refused combo, the clipboard write before a paste failed:reason: clipboard_failed) says "not sent" in its note and carriesdispatched: false. Avia=pastereceipt says whether the clipboard was replaced where that stage answered (a lost or cut-short reply says nothing about it: absence means unknown), also when the paste chord after it failed; a completed clipboard write is an observed effect of its own and keeps the mark (clipboard_done), as a switch does (activation_done). The fields a receipt carries follow what happened: a refusal hastarget,reasonand who is in front; a failed helper hasfront_at_dispatch; a dispatched input hasdelivered_to. - On a local or SSH Mac,
cliclickexits 0 and only warns on stderr when the posting process lacks the Accessibility grant (the OS drops the event) or when a character has no key code in the active keyboard layout (nothing is typed; measured 27.10.2026 under a Cyrillic layout); the paste chord program warns the same way and then posts nothing. Such receipts areok: falsewithreason: permission_denied/untypable_charactersand no input mark (a completed clipboard write or switch before it keeps its own,clipboard_done/activation_done); the hint names the method that failed (typing, a shortcut, the paste chord). - A switch that happened before a failed or dropped input keeps the mark
(
activation_done): the front changed, which is an observed effect. - An input of several steps that fails after earlier steps succeeded returns
ok: falsewithsteps_doneand keeps the mark. A failed or timed-out first step, a refusal, reads,waitandremote_execcarry no input mark (a switch or clipboard write that completed before keeps its own). A helper the OS could not start (helper_not_launched) never ran:dispatched: false.activate_appcarries the mark only when the front actually changed to the target; already in front isactivated: falsewithout a mark.
Observation without images (ax_tree, window_list):
- macOS: a pure-ctypes Accessibility client runs in a short-lived helper
process (
macos_native.py), so a stuck application can be cut off and a native fault never reaches the host. Each node is read in one multi-attribute call under a messaging timeout set for the whole helper process (through the system-wide element, so it reaches every element the walk touches); the walk is breadth-first from the focused window (root=focusedstarts at the focused element), stops atmax_nodes(default 150, ceiling 2000) ortimeout_ms(default 3000, ceiling 15000) and names every cut intruncated_by: a node's children are requested up to the remaining node budget plus one, so a wide list is cut bymax_nodes(named whenever the budget left children unrequested, whether or not the visited count reached it) and the node recordschildren_unread; a node or a children list the application failed to answer is a cut too (read_errors: that subtree is not requested,errorscounts the failures by attribute); web content (AXWebArea) is skipped unlessinclude_web=true.app=is resolved in the same helper process. Nodes carryid,depth,parent_id(absent on the root), role,label(the title, else the description; at most 200 characters and no length of its own: the whole texts aretitleanddescription),titleanddescriptionseparately,value,url(a link's AXURL as an absolute string), bounds,enabled: falseandfocused: true(only those values), all from the same one read per node.text_limit(default 200, ceiling 100000) is the characters kept per title, description, value and url; a cut one of those four gives its whole length in<field>_len, so a largertext_limit(orwindow=/root=focusedto narrow the read) brings the rest. An attribute the application failed to answer (an error other than ordinary absence) is counted inerrorsby attribute, and an element none of whose attributes could be read is a failed read, not an empty node;marksnumber the elements of the marked roles with centre points in input space (raw=true): a mark is a position, not proof that the element reacts to a click. A failed read isok: falsewith its reason (not_running,accessibility_untrusted,no_window, a busy application,accessibility_read_failedwhen not one node could be read); it never answers with a process list. Measured on 27.10.2026: the old AppleScriptentire contentswalk took 18–31 s on a 37-node window; this reads it in 1.6 s cold and 50 ms warm. window_listnever renders an unread fact asfalseor[]: a window list the Accessibility API refused sayswindows_unread(a window attribute it refused is listed in that window'sunread, never shown asfalse; a window nothing of which could be read hasunread: {window: <error>}), an unread front leaves the per-rowfrontmostout and saysfront_unread(on every backend), a listing that failed isok: false. On X11 and OSWorld a row isfrontmostwhen it is the active window, not merely a window of the active process.- Linux: no element reader in this skill (
ok: false,reason: unsupported, with the window rows); OSWorld remotes return the guest's whole accessibility tree (opt-in in the bridge;app,root,max_nodes,timeout_msdo not apply there); SSH Macs answerunsupported. - Who is in front is read fresh on every call (macOS
lsappinfo, X11xdotool getactivewindowmatched to thewmctrlrows), never from a cached AppKit value.
Backend matrix (honest limitations):
- macOS:
screencapture+cliclick+osascript;sipsfor downscaling;lsappinfofor the frontmost application; the native helper for windows, activation, the element walk, grants and displays. Activation tries AXAXFrontmost, thenNSRunningApplication, then System Events, accepting each only after a fresh read shows the target in front withinwait_ms(a request the OS accepts and ignores, or completes slower than that, falls through to the next); the System Events step can raise a one-time Automation consent prompt.scrollis unsupported (cliclick has no scroll-wheel command — page viakeywithpage-down/page-up);middleclick and non-leftmouse_down/mouse_upare unsupported via cliclick.hold_keyholds PURE-MODIFIER combos only (cmd,cmd+shift, ...): cliclickkd:/ku:accept only modifiers andkp:cannot hold — non-modifier holds report an honest error. Inkeycombos the base key is tapped while modifiers are held (cmd+s= hold cmd, tap s). On a local or SSH Mac and on X11 a letter's case under a modifier adds no Shift (cmd+S=cmd+s; Shift is named:cmd+shift+s), and a bareAis a capital.type_textwithvia=pastewrites the clipboard (pbcopy, given UTF-8 and told so whatever locale the process inherited, as over SSH) and pastes with Command and the key that givesvunder Command in the active keyboard layout, with exactly the Command flag (anosascriptJavaScript program, the same over SSH, posting under the Accessibility grant cliclick needs; no Automation grant), after the target was verified in front (a refusal leaves the clipboard alone; the receipt says whether the clipboard was replaced, also when the paste chord after it failed); use it for long or non-ASCII text, whichcliclick t:types slowly. Neither the layout's typedvnor a fixed US key is that key everywhere (measured on an AppKit Edit menu: Russian – PC, Dvorak – QWERTY ⌘, Dvorak); a layout with none (Turkmen) isuntypable_charactersand nothing is posted.helper_stderrnames the key sent; a dispatched paste does not prove the focused element took it.viaacceptstypeorpaste; anything else is refused. - Linux X11: key/hold validation also requires loadable
libX11(discovered byctypes.util.find_library); missing validation refuses without sending a prefix.xdotool(input and the active window),gnome-screenshot/scrot(capture),wmctrl(window rows andapp=activation throughwmctrl -i -a, verified withxdotool getactivewindowmatched to the rows, so nogetwindowclassnameis needed), ImageMagick (downscaling). Targeting needs both wmctrl and xdotool: without xdotool a switch cannot be verified and targeted inputs are refused. A name is matched exactly against the WM_CLASS aswmctrl -xprints it (instance.Class: the whole string, the part before the first dot or the part after a dot, soFirefoxnamesnavigator.FirefoxandGimp-2.10namesgimp-2.10.Gimp-2.10; a fragment between dots never does), the window title orpid:<n>/0x<id>; a class is every window of that class,pid:every window of the process, a title or id that one window, and a window the X server has no pid for is itself. A class that is not in front is brought forward through its first-listed window (wmctrllists the oldest first): name a window by its title to choose one. Window rows are parsed fromwmctrltext, so a title that contains a newline can forge a row (a web page title cannot). Function keys and case-sensitive keysyms are supported (f5→F5,XF86AudioPlayas-is). - Linux Wayland:
ydotool(pointer + typing; requires a runningydotoold) orwtype(typing only),grimorgnome-screenshot(capture).xdotooldoes NOT work on Wayland.key,hold_key,app=targeting andwindow_listare unsupported on Wayland (the compositor exposes no window list or activation to other programs; withwmctrlpresent only XWayland windows are listed, and the result says so); the refusal names the session so the owner can bring the application forward. - Remote OSWorld HTTP:
screenshotusesGET <target>/screenshot; input andremote_execusePOST <target>/execute, matching OSWorld's in-VMpyautoguichannel. The runner writes anosworld_httpconnection before each task.app=is resolved from the guest'swmctrl -lpxGrows (the same exact matching as local X11), activated by window id (wmctrl -i -a) and verified by pollingxdotool getactivewindowup towait_ms; every input's guest program reads the active window (id|pid|title, twoxdotoolcalls per read, a third for a window without a pid, the title base64-encoded so that nothing a window is called can look like a marker) before and after the action in the same call and withholds a targeted input when the active window is not one of the target's windows (target_lost) or could not be read (front_unread); a switch observed earlier in the call keeps its mark. The probe cannot raise: for an untargeted input a probe failure leaves the place unknown and never fails the action. A targetedtype_textmakes one switch for the whole call; a withheld or refused paste is final (never retried by typing); a clipboard program that ran and failed (pypercliporxclipabsent) falls back totypewriteonce with the same switch; a remote call that did not answer (transport_failed: the paste may have run) is final, nothing is retyped, with or withoutapp=. Withapp=""the action, its exit-code reading and the clipboard/typewrite routing are otherwise unchanged, and two calls the base sent are refused before any call: a provided negative point formouse_down/mouse_up(the base pressed at the pointer) and a point that a stored transform of another display maps outside the guest (the base clicked the scaled point); what changed on the benchmark path is the probe around the action (markers are stripped from the returned output),activate_appin the tool list,appin the input schemas andwindow_listanswering parsed rows from a base64-encoded listing (the last line before its separator, so a login-shell greeting cannot hide the windows;ok: falsewhen the guest printed no listing or one that does not decode); no text a window shows can reach the readers as a separator or a marker. Honesty guarantees on this backend:scrollclicks are 1:1 wheel detents (not multiplied); non-ASCII, angle brackets, multi-line or longtype_text(orvia=paste) is pasted via the in-VM clipboard (pyperclip+ctrl+v, ASCIItypewritefallback) so Unicode is not silently dropped; every action'sokreflects the guest processreturncode/status(HTTP 200 alone is not success);keybackspacepresses BackSpace andfwd-deletepresses forward Delete, and a letter reaches pyautogui as written (ctrl+C=hotkey('ctrl', 'C')); screenshots are capped at 20 MB and PNG-validated before use. - Remote SSH macOS:
screenshotuses remotescreencapture+scp; input usescliclickon the Mac, wrapped so the same ssh call reads the frontmost application before and after and withholds a targeted input when the target is no longer in front.app=is resolved from the Mac'slsappinfo list(name, bundle id,pid:<n>; nothing is launched) and activated by pid through System Events (only the integer pid enters the remote command, never the name), verified by pollinglsappinfo frontup towait_ms; an input cliclick cannot express is refused before any switch; a transport failure after a switch still reports the switch; an ssh call that ended without the wrapper's completion marker (a refused connection, a channel broken after the input may have run:ssh_failed) leaves the outcome unknown (dispatched: null,transport_failed; a clipboard marker that did come back is kept as a fact), never "not sent". cliclick's stderr is read the same way as locally.type_textwithvia=pastewrites the Mac's clipboard (pbcopy, told the text is UTF-8 whatever locale ssh forwarded) and pastes with the same chord program through the Mac's ownosascriptin the same ssh call, and the receipt says whether the clipboard stage completed (a failed write sends nothing:clipboard_failed,dispatched: false).window_listlists applications and the frontmost one (no window titles over SSH); a listing that failed or named nothing isapps_unread, an unread front saysfront_unread. The remote Mac must have Screen Recording, Accessibility and (for activation) Automation grants configured by the owner. This skill does not install or store private SSH keys.
Connection registry:
- Connections live in
data/state/skills/unix_computer_use/connections.json. - Default active connection is
local. add_connectioncan addosworld_httporssh_macosconnection metadata, but it never accepts private key material.- For SSH, configure
~/.ssh/config,IdentityFile, and/orssh-agentyourself. If auth fails,test_connectiontells you what to fix. - Agents should add/test/activate connections only when the user or a benchmark runner provides an explicit target. They should not scan for arbitrary remote machines.
- A disabled active connection FAILS CLOSED: action tools return an error rather than silently falling back to the local desktop (an action aimed at a remote target must never land on the host).
Permissions note: the remote backends require the net permission. net needs
no owner grant (skill grants only gate inject_chat/subscribe_event), and the
launcher names this skill among the bundled seeds that start enabled anyway; the
network is used for configured connections, which the owner, a runner or the
agent can add via add_connection. Agents should use only explicitly provided
targets as described above. An owner who turns the skill off keeps it off across
updates. The reads declared in the manifest (window_list, ax_tree,
capabilities, cursor_position, list_connections) skip the per-call safety
check and are available to observe-only consciousness (owner decision
27.10.2026); screenshot and every input stay checked.
Layout: plugin.py (registration, coordinate contract, local input),
place.py (targets, activation, the receipt composer _finish shared by every
transport, window rows, element reads), remote.py (OSWorld and SSH
transports), macos_native.py (the helper process), paste_chord.js (the
macOS paste chord, local and over SSH). The skill intentionally
does not hide OS permission requirements, and missing backends produce explicit
errors with the capability report instead of guessing. The agent should prefer
semantic application APIs when available and should ask the human before
destructive or sensitive UI actions.
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.

