Recipes

Real desktop workflows with Cua Driver, each with the prompt to give your agent and how to check the result. The last section shows how to design your own cursor motion.

Each recipe assumes Cua Driver is installed and connected to your agent (Quickstart). The recorded ones ran on macOS with GPT-6 Astra + Cua Driver: selected gestures play at 1x; setup, waits, retries, and intervening edits are cut. They use foreground desktop input and fictional data, so run them on a desktop you are not using, or in a VM.

Cua Driver has no file or HTTP tools: an agent reads and writes files by driving apps (an editor, Terminal, a PDF viewer). To run many jobs in parallel, give each its own Cua Sandbox.

Background examples on Windows and Linux#

Agents operating desktop apps while the user's active window stays in place, recorded by the Cua team on the named platform.

Build and check a WPF app on Windows

Claude Code builds a WPF CRM, runs it, patches the code, and checks the app again.

View the original post on X
Fill a Linux spreadsheet over SSH

An agent enters a monthly budget in Gnumeric on a remote machine with no attached display or GPU.

View the original post on X

Build and inspect a map#

QGIS map and LibreOffice Writer handoff from eight fictional station observations. Inputs: stations.csv and campus.geojson.

Build a planning map in QGIS and inspect the exported PDF

Selected layout gestures in QGIS and inspection of the exported map in Chrome.

Use Cua Driver to build a fictional Morrow Field campus planning map in QGIS.
Preserve stations.csv and campus.geojson. Save new outputs in an output folder.
 
Import stations.csv as points: X = longitude, Y = latitude, source CRS EPSG:4326.
Import campus.geojson with the same source CRS. Set the project CRS to EPSG:3857
for a layout with distances in meters; do not relabel the source coordinates.
 
Use the field calculator to create status: READY when flow_lpm >= 2.0 AND
days_since_check <= 30; otherwise REVIEW. Compare the original numeric values
without rounding or recalculating elapsed days. Save an editable GeoPackage.
 
Style READY green and REVIEW amber, label all eight station IDs, and include
category names so the result does not rely on color alone. Select the REVIEW
features and confirm their IDs and count from the data.
 
Create one A4 landscape layout with a title, legend, north arrow, and meter scale
bar. Save a QGZ project with relative paths and export the map as a PDF.
 
In LibreOffice Writer, create a one-page handoff with the map, verified category
counts and REVIEW IDs, the classification rule, and the fictional-planning caveat.
Save the handoff as ODT and PDF. Reopen the project and inspect both exported PDFs
in Chrome or Preview. Report the saved files, completed checks, and any unresolved issue.
Keep the outputs local; do not send or publish them.

Check: R01 sits at longitude 0.00150, latitude 0.00440 (swapped axes look plausible here). READY is R01–R04 and REVIEW is R05–R08; R08 fails on flow 1.99. The map PDF is A4 landscape with all eight labels, legend, north arrow, and scale bar.

Redact and annotate a PDF#

A review copy of a fictional two-page proof in Preview, plus a Writer handoff. Input: willowfen-vendor-proof-SOURCE.pdf.

Redact and annotate a vendor proof in Preview

Selected redaction and annotation steps in Preview.

Use Cua Driver to prepare a local review copy of willowfen-vendor-proof-SOURCE.pdf
in Preview. Preserve the original. Duplicate it before editing, and confirm that
the active document is the copy before applying any irreversible redaction.
 
Use Preview's actual Redact tool to remove the complete line below each marker:
- Page 1, R1: Supplier cost.
- Page 1, R2: Internal reference.
- Page 2, R3: Supplier contact.
- Page 2, R4: Internal contingency.
The R1-R4 markers may remain. Do not substitute shapes or highlights for Redact.
If Redact is unavailable, stop and report the limitation.
 
Keep the title, date, time, location, participant count, public fee, materials,
booking address, schedule, access notes, and complete room diagram. Add REVIEW
COPY in page 1's outlined annotation area without covering other text. Save as
willowfen-vendor-proof-REVIEW.pdf, then close and reopen it in Preview.
 
In LibreOffice Writer, create a one-page handoff with the workshop title,
review-only status, source and review filenames, the four removed categories
without their values, retained public facts, page count, and verification results.
Save willowfen-release-handoff.odt and export willowfen-release-handoff.pdf.
 
Inspect both review PDF pages and the handoff PDF in Preview. Report the files
and the checks that passed. If extraction has not been checked, state that it
remains unverified. Keep all outputs local; do not send or publish them.

Check: the source hash is unchanged; the four target lines are gone from the rendered pages and from pdftotext output. Extraction alone is not proof: a black box can hide text that is still recoverable.

Prepare an exhibition panel#

Crop a fictional archive ticket in GIMP, build two editable A4 panels in Inkscape, and a two-page installation packet in Writer. Inputs: archive-contact-sheet.png and panel-starter.svg.

Prepare archive artwork in GIMP and an exhibition panel in Inkscape

Selected image and panel edits in GIMP and Inkscape.

Give the agent this caption table with the prompt:

StationObject IDYearCaption
ARA-1932-011932A three-pence ticket links Ferry Steps and Market Square.
BRA-1956-021956Route 4 connects Ferry Steps, Market Square, and Hill Library.
CRA-1974-031974Three morning departures make the same journey in twenty minutes.
Use Cua Driver to prepare a fictional Riverline Archive / Everyday Journeys
exhibition. Preserve both source files and save new work in an output folder.
 
In GIMP, crop the ticket from archive-contact-sheet.png: top-left origin,
X = 200, Y = 300, width = 1800, height = 1100 pixels. Retain all ticket text;
do not scale or retouch it. Save riverline-hero.xcf and export riverline-hero.png.
 
In Inkscape, use panel-starter.svg to create two A4 landscape panels. Keep text
editable, content inside the 15 mm safe margin, body text at least 24 pt, and
headings at least 36 pt. Use teal #164E52, oxblood #702F3B, and ivory #F5EEDC.
The entry panel must say Riverline Archive, Everyday Journeys, START HERE,
A > B > C, and Fictional collection. The object key must include all three
stations, object IDs, years, and exact captions from the supplied table.
Save riverline-entry.svg and riverline-object-key.svg. Export each as PDF and
as a 1782 × 1260 PNG.
 
In LibreOffice Writer, create an A4 portrait installation packet with exactly
two pages and an explicit page break. Page 1 needs the exhibition title,
proportional ticket image, caption table, and fictional-collection notice.
Page 2 needs both panel images and this illustrative plan: a 3600 mm wall;
A, B, and C centered at 600, 1800, and 3000 mm from its left end; object centers
1400 mm high; label centers 1200 mm high; and 1500 mm of clear approach depth.
Place the entry panel left of A and the object key right of C. Include a checklist
to confirm object order, measure centers, keep the approach clear, proof labels,
and check glare. Label these as illustrative targets, not measured venue
conditions or formal accessibility compliance.
 
Save riverline-installation.odt and export riverline-installation.pdf. Reopen
the editable files in their apps and inspect the exported PDFs in Preview.
Report output files, completed checks, and unresolved issues. Keep the outputs
local; do not send or publish them.

Check: the XCF canvas is 1800 × 1100 with all ticket text; SVG text is still editable; the packet PDF has exactly two pages with every measurement.

Build a report in Numbers#

macOS only (Numbers has no Linux or Windows version).

Using cua-driver, fetch the last 30 days of AAPL daily closing prices. Open Numbers, create a new spreadsheet, enter Date and Close columns, add a 2D line chart, then export the document as a PDF to ~/reports/aapl-analysis.pdf.

The agent fetches data by driving Terminal (curl) or a browser, then drives Numbers with launch_app, click, type_text, and set_value. Check: open ~/reports/aapl-analysis.pdf and confirm the table and chart.

Automate a legacy Windows app behind a VPN#

Run Cua Driver on the Windows machine inside the VPN, so the desktop session, app traffic, and files stay there.

Drive a legacy postal app with Cua Driver

Claude Code fills shipment details and prints receipts in a Windows desktop app with no API.

View the original post on X

Confirm the driver sees the app first:

cua-driver call list_apps
cua-driver call list_windows

An empty [] means the driver is outside the interactive desktop; see Windows over SSH. Stage input files on that machine, then:

Open the payroll client, log in with the credentials in C:\creds.txt, create a new employee from new_hire.xlsx, generate the onboarding report, and export it as PDF to the Desktop.

Export contacts overnight#

Log into LinkedIn or X yourself in the browser the agent will drive, keep the machine awake, and cap the run while testing:

Using cua-driver, open the browser to https://www.linkedin.com/mynetwork/invite-connect/connections/ .
 
For each of the first 50 connections:
1. Open the profile.
2. Read first name, last name, role, company, and profile URL.
3. Append one row to ~/contacts.csv.
4. Set met_at to "Google Devfest Toronto" for every row.
 
Use this CSV header:
first,last,role,company,met_at,linkedin
 
If a value is missing, leave that field empty and keep going.

Spot-check ~/contacts.csv in the morning before importing it anywhere.

Fill a form from a local file#

The data stays on your machine: the agent opens the file in a viewer, reads it on screen, and types it into the form. Name the exact fields you want.

Open ~/Downloads/resume.pdf in the PDF viewer. Read the name, email,
phone, and work history. Then open
https://form.jotform.com/<your-form-id> in the browser, fill each matching
field from the PDF, and submit the form.

Watch the field mapping and correct it in plain language ("use the resume email for Work Email").

Design a cursor motion#

This one is for developers, not agents. Cua Driver plans every agent cursor move with Cua Cursor Motion, an MIT-licensed library you can use on its own. It comes in two packages that produce the same trajectories:

  • cua-cursor-motion (libs/cua-driver/rust/crates/cua-cursor-motion), the Rust crate the driver plans every move with;
  • @trycua/cursor-motion (libs/typescript/cursor-motion), a TypeScript port with a canvas player that draws the Cua cursor.

Both are tested against the same golden trajectories, so a motion you tune on a web page plays the same way in the driver. Neither is published to crates.io or npm. Use them from trycua/cua.

In Rust, add a git dependency. Cargo finds the crate inside the repository:

[dependencies]
cua-cursor-motion = { git = "https://github.com/trycua/cua" }

On the web, build the single-file ES module once and copy it with its types into your project. The file has no imports, so it also loads from a plain <script type="module">.

git clone --depth 1 https://github.com/trycua/cua
cd cua/libs/typescript
pnpm install
pnpm --filter @trycua/cursor-motion build
cp cursor-motion/dist/index.js   ../../../my-app/src/vendor/cua-cursor-motion.js
cp cursor-motion/dist/index.d.ts ../../../my-app/src/vendor/cua-cursor-motion.d.ts

Plan a move#

A move is planned once, as samples at 120 Hz, then played back by time. It takes a style (signature_arc, spring_settle, magnetic, comet_swoop, adaptive, or classic), a timing mode (native, fitts, or fixed), and the shape knobs of set_agent_cursor_motion.

import { planMove } from './vendor/cua-cursor-motion.js';
 
const trajectory = planMove(
  { style: 'spring_settle', timing: 'fitts' },
  { from: { x: 100, y: 100 }, to: { x: 700, y: 400 }, target: [660, 380, 80, 40] }
);
const { x, y, heading } = trajectory.sampleAt(0.3);
use cua_cursor_motion::{plan_move, MotionParams, MotionStyle, MotionTiming, MoveRequest, Pt};
 
let params = MotionParams { style: MotionStyle::SpringSettle, timing: MotionTiming::Fitts, ..Default::default() };
let request = MoveRequest {
    target: Some([660.0, 380.0, 80.0, 40.0]),
    ..MoveRequest::new(Pt::new(100.0, 100.0), Pt::new(700.0, 400.0))
};
let sample = plan_move(&params, &request).sample_at(0.3);

arrivalT (arrival_t in Rust) is when the tip first reaches the target. Cua Driver clicks then and lets any follow-through or bounce finish during the click. The target rectangle sets the Fitts width: smaller targets take longer.

The Cua Driver Python and TypeScript SDKs export the same planner through the driver's native boundary (plan_cursor_move and planCursorMove), so a host can plan moves with the Rust code itself. A planned move is a CursorTrajectory: read its samples once with samples() and interpolate locally each frame.

import cua_driver as cd
 
params = cd.default_cursor_motion_params()
params.style = cd.CursorMotionStyle.SPRING_SETTLE
trajectory = cd.plan_cursor_move(
    params,
    cd.CursorMoveRequest(
        from_point=cd.CursorMotionPoint(x=100, y=100),
        to_point=cd.CursorMotionPoint(x=700, y=400),
        from_heading=None,
        end_heading=None,
        target=cd.CursorMotionRect(x=660, y=380, width=80, height=40),
        seed="demo",
        reduced_motion=False,
    ),
)
samples = trajectory.samples()

Play it on a web page#

MotionPlayer draws the Cua cursor, its glow, the comet trail, and the click ripple inside a <canvas>. It never moves, hides, or restyles the page's own mouse pointer.

import { MotionPlayer } from './vendor/cua-cursor-motion.js';
 
const player = new MotionPlayer(document.querySelector('canvas')!);
player.place({ x: 80, y: 300 });
await player.moveTo(
  { x: 640, y: 120 },
  { params: { style: 'comet_swoop' }, target: [600, 104, 80, 32], click: true }
);

Design your own motion#

A custom motion is a MotionSpec: a path shape (straight, arc, bow), a speed curve (named easings or cubic_bezier), an overshoot or settle (follow_through, spring), a duration model (fitts, fixed, distance), a heading mode, effects, and a trail. signature_arc, spring_settle, and comet_swoop are specs themselves, so start from one:

import { planSpec, specForStyle } from './vendor/cua-cursor-motion.js';
 
const spec = specForStyle('signature_arc')!;
spec.ease = { type: 'cubic_bezier', x1: 0.3, y1: 0, x2: 0.1, y2: 1 };
spec.settle = { type: 'follow_through', amount: 0.04, maxPt: 14, at: 0.75 };
spec.effects.trail = true;
 
const trajectory = planSpec(spec, { from: { x: 100, y: 500 }, to: { x: 900, y: 200 } });

Custom specs play in your own renderer. Cua Driver itself takes one of the six styles and its knobs.

Try it in the playground#

The playground lets you pick a style, drag the start and end points, tune the curve with a live speed plot, and copy the result as TypeScript or as Cua Driver config. Run it from a checkout; it opens at http://127.0.0.1:4173/playground/.

cd libs/typescript
pnpm install
pnpm --filter @trycua/cursor-motion playground

For a built-in style, the exported Cua Driver config looks like this:

cua-driver config set cursor.motion.style comet_swoop
cua-driver config set cursor.motion.timing fitts
cua-driver cursor motion --session demo --style comet_swoop --timing fitts --effects trail=on

The arc knobs (arc_size, arc_flow, start_handle, end_handle) and the classic glide's spring and turn_radius travel only through set_agent_cursor_motion. See Choose your cursor for how saved defaults, start_session, and per-call settings combine.