Script API
This page organizes the script API by task. For live autocomplete and complete signatures in your editor, add this line at the top of a script:
/// <reference path="./.cursorcrane/cursorcrane.d.ts" />Start a Script
Section titled “Start a Script”Every script needs meta and run(ctx):
/** @type {CursorCrane.Meta} */const meta = { type: "action", name: "Example", permissions: [],};
/** @param {CursorCrane.Context} ctx */async function run(ctx) { ctx.toast("Done");}meta field | Purpose |
|---|---|
type | "action" performs a one-off action; "form" opens a form. |
presentation | Optional for forms. "panel" suits continued work; "transient" closes on focus loss. |
name | The script name shown in Cursor Crane. |
permissions | The permissions required by the script. |
Available permissions are "accessibilityAPI", "clipboard", "keyboard", "mouse", and "shell". An API cannot be used unless the script declares its required permission.
Find Apps and Windows
Section titled “Find Apps and Windows”Use ctx.applicationManager to inspect running apps:
| Member | Purpose |
|---|---|
applications | All available apps. |
getApplicationByBundleIdentifier(id) | Finds an app by bundle identifier. |
getApplicationByPid(pid) | Finds an app by process ID. |
Each Application has name, bundleIdentifier, and pid.
Use ctx.windowManager to find and switch windows:
| Method | Purpose |
|---|---|
getWindowsByPid(pid) | Gets an app’s windows. |
getWindowsByBundleIdentifier(id) | Gets windows by app. |
getWindowById(windowId) | Finds a window by ID. |
getActiveWindow() | Gets the window currently in use. |
getPreviousWindow() | Gets the previously used window. |
activateWindow(windowId) | Switches to a window. |
Window has title, windowId, and its application.
Find and Control Interface Elements
Section titled “Find and Control Interface Elements”ctx.elementInspector.getElementByPid(pid) gets an app’s interface-element entry point and requires accessibilityAPI. From there, use getWindows() to find the window, then find its buttons, text fields, and list rows.
const application = ctx.elementInspector.getElementByPid(window.application.pid);const root = application?.getWindows().find( item => item.windowId === window.windowId);const saveButton = root?.queryElements('Button[title="Save"]')[0];Element Information
Section titled “Element Information”| Property | Purpose |
|---|---|
title | The title provided by the element. |
displayTitle | A title that is more suitable to show to a person. |
label, description | Additional information about the element. |
kind | The element type, such as Button or TextField. |
windowId | The ID of the window containing the element. |
rect / frame | The element’s on-screen position and size. |
Traverse and Query Elements
Section titled “Traverse and Query Elements”| Method | Purpose |
|---|---|
getChildren() | Gets direct children. |
getVisibleChildren() | Gets children that are currently visible. |
getParent() | Gets the parent element. |
getFocusedWindow() | Gets the app’s focused window. |
getWindows() | Gets the app’s windows. |
getWindow() | Gets the element’s containing window. |
queryElements(selector, maxCount?) | Searches all elements in a window. It traverses children and can block noticeably on a large hierarchy. |
queryVisibleElement(selector, maxCount?) | Searches only the currently visible content. |
getStringValue() | Gets the current value as text, such as the content of a text field. |
getIntValue() | Gets the current value as an integer. |
getDoubleValue() | Gets the current value as a floating-point number. |
See Element Selectors for selector syntax.
Element Actions
Section titled “Element Actions”These methods require accessibilityAPI and can be awaited:
| Method | Purpose |
|---|---|
performPress() | Triggers the element’s main action. |
setStringValue(value) | Sets a new text value on an element. |
setIntValue(value) | Sets a new integer value on an element. |
setDoubleValue(value) | Sets a new floating-point value on an element. |
performClick() | Moves to an element and clicks it. |
performMoveCursorTo() | Moves to an element. |
performShowMenu() | Opens an element’s menu. |
getActions() | Lists the actions supported by an element. |
performAction(identifier) | Triggers a particular action. |
Choose the getter or setter that matches the value type used by the element.
Keyboard, Mouse, and Feedback
Section titled “Keyboard, Mouse, and Feedback”ctx.keyboard
Section titled “ctx.keyboard”Requires keyboard. All three methods return a Promise:
await ctx.keyboard.pressKey("a", ["command"]);await ctx.keyboard.releaseKey("a", ["command"]);await ctx.keyboard.pressAndReleaseKey("return", []);Use "shift", "control", "option", and "command" as modifiers.
ctx.mouse
Section titled “ctx.mouse”Requires mouse:
| Method | Purpose |
|---|---|
moveTo(x, y) | Moves the pointer smoothly. |
warpTo(x, y) | Moves the pointer immediately. |
click(button, modifiers?) | Clicks; 0 is left, 1 is right, and 2 is middle. |
doubleClick(button, modifiers?) | Double-clicks. |
rightClick(modifiers?) | Right-clicks. |
ctx.shell
Section titled “ctx.shell”Requires shell. execute(command) runs a non-interactive zsh command and returns its output:
const result = await ctx.shell.execute("git status --short");if (result.exitCode === 0) { ctx.toast(result.stdout || "Working tree is clean");}The result contains stdout, stderr, and exitCode. Commands share the script’s execution timeout, and output is limited to 1 MiB per stream.
ctx.clipboard
Section titled “ctx.clipboard”Requires clipboard. Read or replace the plain text on the system clipboard:
const text = ctx.clipboard.readText();if (text !== null) { ctx.clipboard.writeText(text.trim());}readText() returns null when the clipboard does not contain plain text. writeText(value) replaces the current clipboard contents with the provided text.
ctx.toast and console
Section titled “ctx.toast and console”Use ctx.toast(message) for brief feedback. ctx.toast.highlight(rect, text?, timeout?) highlights an area on screen and can show a short hint below it.
ctx.toast("Finished");ctx.toast.highlight(element.rect, "Save button", 3);console.log(), error(), warn(), info(), and debug() write to the script log. Use them to check progress and errors while you develop a script. Console methods keep their usual behavior and also appear in Cursor Crane’s script log.
setTimeout and clearTimeout
Section titled “setTimeout and clearTimeout”Use setTimeout(callback, delay?, ...args) to run a callback once after at least the specified number of milliseconds. It returns an identifier that can be passed to clearTimeout(identifier) before the callback runs.
const timeout = setTimeout((message) => { ctx.toast(message);}, 500, "Still working");
// Cancel it when the delayed work is no longer needed.clearTimeout(timeout);Timers remain available while the script execution is active. A form keeps its execution active until the form is closed, so a timer can update or hide an open form. Closing the form cancels timers that have not run yet.
Create a Form
Section titled “Create a Form”A form script returns an array from run(ctx). Every item needs a unique id:
function run(ctx) { return [ { id: "selector", kind: "textInput", title: "Selector", value: "Button", }, { id: "run", kind: "button", title: "Query", submit: true, onClick: formCtx => { formCtx.toast(`Searching for ${formCtx.formData.selector}`); }, }, ];}For an async form, Cursor Crane opens the form window immediately and shows a loading indicator until run(ctx) resolves. The initial form run(ctx) has no timeout, so it can wait for slow setup work before returning the items.
| kind | Common fields | Best for |
|---|---|---|
label | text, value? | Showing instructions or results. |
numberInput | title, value?, min?, max?, step? | Entering a number. |
textInput | title, value?, placeholder? | Entering one line of text. |
select | title, value?, options | Choosing from a list. |
checkbox | title?, value? | Checking an option. |
toggle | title?, value? | Turning an option on or off. |
button | title, submit?, onClick? | Performing an action. |
Editable items can provide onChange(newValue, ctx); buttons use onClick(ctx). Both can be async functions.
A form ctx also provides:
| Member | Purpose |
|---|---|
formData | Reads the current form values. |
updateForm(items) | Replaces the form. A matching item without a new value preserves the person’s current input. |
hideForm() | Hides a panel without ending its session. Invoking the same script again shows the existing form and preserves its state. A transient form closes instead. |
closeForm() | Closes the current form. |
onFormActivate(callback) | Registers a callback for when a hidden panel is shown again. The callback receives the current form context and can be async. |
Use onFormActivate to refresh temporary state when a person returns to a hidden panel. It does not run for the first presentation, and it does not apply to transient forms because they close instead of being reused. Register it from run(ctx):
function run(ctx) { ctx.onFormActivate(async formCtx => { await refreshResults(); formCtx.updateForm(renderForm(formCtx)); });
return renderForm(ctx);}People can use Tab, Shift + Tab, and arrow keys to move through a form. When a button is focused, Enter or Space activates it. A button marked submit: true can be activated from anywhere with Command + Enter.
For a panel, Esc and the yellow title-bar button hide the form, while the red button and Command + W close it and end its session. Invoking a hidden panel script brings that same form back instead of creating another window. A transient form closes when it loses focus.
Handle Errors
Section titled “Handle Errors”Use try / catch while developing a script to show a clear message:
try { await ctx.mouse.moveTo(100, 100);} catch (error) { ctx.toast(String(error));}If a script calls an API without the required permission, uses an invalid selector, or cannot reach its target, that call fails. Add a toast or a form message for common failures so the script stays easy to use.