Script API
/// <reference path="./.cursorcrane/cursorcrane.d.ts" />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 the script requires |
Permissions#
Available permissions are "accessibilityAPI", "clipboard", "keyboard", "mouse", and "shell".
An API cannot be used unless the script declares its required permission, so a script that reads elements and clicks them needs both accessibilityAPI and mouse. Declaring them up front also means anyone reading the script can see its reach at a glance.
These are separate from the macOS permissions the app itself needs, described in Permissions.
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 |
A Window has title, windowId, and its application.
Find and control interface elements#
ctx.elementInspector.getElementByPid(pid) returns an app's element entry point and requires accessibilityAPI. From there, use getWindows() to find the window, then query its buttons, text fields, and 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];Selector syntax is covered in Element Selectors.
Element information#
| Property | Purpose |
|---|---|
title |
The title provided by the element |
displayTitle |
A title more suitable to show a person |
label, description |
Additional information about the element |
kind |
The element type, such as Button or TextField |
windowId |
The ID of the containing window |
rect / frame |
On-screen position and size |
Traverse and query elements#
| Method | Purpose |
|---|---|
getChildren() |
Gets direct children |
getVisibleChildren() |
Gets children that are currently visible |
getParent() |
Gets the parent element |
queryElements(selector) |
Finds descendants matching a selector |
Prefer getVisibleChildren() when you only care about what the user can see, since a large hidden subtree costs time to walk.
Next#
- Using Scripts to bind your script to a key
- Scripting index