# @kentcdodds/cursor — agent notes Human setup and intent live in [`README.md`](./README.md). This file is for agents: imports, smoke checks, snippets, and edge cases. Secrets by **name** only — never paste key values. Do not disable live webhooks or jobs. ## Secrets | Name | Scope | Required | Notes | | --- | --- | --- | --- | | `cursorApiKey` | user | Yes | Raw key from cursor.com/dashboard → API Keys | | `githubPersonalAccessToken` | user | No | Only for `agentsWithOpenGithubPrs` | Host: `api.cursor.com`. Placeholder: `{{secret:cursorApiKey}}`. ## Import paths | Export | Import | | --- | --- | | overview | `kody:@kentcdodds/cursor` | | request (`cursorRequest`) | `kody:@kentcdodds/cursor/request` | | agents | `kody:@kentcdodds/cursor/agents` | | runs | `kody:@kentcdodds/cursor/runs` | | artifacts | `kody:@kentcdodds/cursor/artifacts` | | workers | `kody:@kentcdodds/cursor/workers` | | models | `kody:@kentcdodds/cursor/models` | | repositories | `kody:@kentcdodds/cursor/repositories` | | me (`apiKeyInfo`) | `kody:@kentcdodds/cursor/me` | | agent-usage (`getAgentUsage`) | `kody:@kentcdodds/cursor/agent-usage` | Prefer static `kody:@kentcdodds/cursor/...` imports from `execute`. Do not lead with `packages.invoke`. Defaults: `./request` → `cursorRequest`; `./agents` → `listAgents`; `./runs` → `listRuns`; `./artifacts` → `listArtifacts`; `./workers` → `getFleetSummary`; `./models` → `listModels`; `./repositories` → `listRepositories`; `./me` → `apiKeyInfo`; `./agent-usage` → `getAgentUsage`. ## Smoke test (read-only) ```js import { apiKeyInfo } from 'kody:@kentcdodds/cursor/me' export default async function main() { return await apiKeyInfo() // => { apiKeyName, userEmail, ... } } ``` Optional read-only follow-ups: `listModels()` from `./models`, or `listAgents({ limit: 5 })` from `./agents`. ## Hand a file to a Cursor cloud agent Prefer **`@kentcdodds/agent-files`** as the durable handoff pipe. Mint a short-lived R2 PUT URL with a tiny MCP `execute` call, PUT the bytes outside MCP (curl/fetch), then put `downloadUrl` in the cloud agent prompt so it can curl/download on the VM. Do **not** rely on CloudAgent / chat image attachments for usable bytes on the VM — `prompt.images` is vision-only. Dropbox is an optional secondary when the file should live in Dropbox; agent-files is the default for agent↔agent binary handoffs. ### 1) Mint upload target (execute) ```js import createUpload from 'kody:@kentcdodds/agent-files/create-upload' export default async function main() { return await createUpload({ filename: 'report.png', contentType: 'image/png', confirm: true, }) // => { key, upload: { method, url, headers }, downloadUrl, expiresAt } } ``` ### 2) PUT bytes (outside MCP) ```bash # Use every header from upload.headers; body is the raw file. curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: image/png" \ # plus remaining keys from target.upload.headers \ --data-binary @./report.png ``` Or in one shot after minting: ```js const target = await createUpload({ filename: 'report.png', contentType: 'image/png', confirm: true }) await fetch(target.upload.url, { method: 'PUT', headers: target.upload.headers, body: fileBytes, // ArrayBuffer / Blob / Uint8Array }) // hand target.downloadUrl to createAgent / createRun ``` ### 3) Include downloadUrl in createAgent / createRun prompt ```js import createUpload from 'kody:@kentcdodds/agent-files/create-upload' import { createAgent } from 'kody:@kentcdodds/cursor/agents' import { createRun } from 'kody:@kentcdodds/cursor/runs' export default async function main() { const target = await createUpload({ filename: 'fixture.zip', contentType: 'application/zip', confirm: true, }) // PUT fileBytes to target.upload.url with target.upload.headers first… const { agent } = await createAgent({ prompt: { text: [ 'Download the fixture and unzip it into ./fixtures before editing.', `curl -fsSL '${target.downloadUrl}' -o /tmp/fixture.zip`, 'unzip -o /tmp/fixture.zip -d ./fixtures', ].join('\n'), // images: [...] // vision-only; not a byte handoff }, repository: 'https://github.com/your-org/your-repo', ref: 'main', }) const { run } = await createRun({ agentId: agent.id, prompt: `Also fetch the same fixture again if needed: ${target.downloadUrl}`, }) return { agentId: agent.id, runId: run.id, downloadUrl: target.downloadUrl } } ``` ## Common snippets Create agent + follow-up run: ```js import { createAgent } from 'kody:@kentcdodds/cursor/agents' import { createRun } from 'kody:@kentcdodds/cursor/runs' export default async function main() { const { agent } = await createAgent({ prompt: 'Add a README with setup instructions', repository: 'https://github.com/your-org/your-repo', ref: 'main', }) const { run } = await createRun({ agentId: agent.id, prompt: 'Also add a troubleshooting section', }) return { agentId: agent.id, runId: run.id } } ``` Billed usage (do not estimate dollars from token counts): ```js import { getAgentUsage } from 'kody:@kentcdodds/cursor/agent-usage' export default async function main() { return await getAgentUsage({ agentId: 'bc-…' }) // cost.rawUsd / cost.chargedUsd from API cost record; chargedCents === 0 → plan-included } ``` Raw escape hatch: ```js import { cursorRequest } from 'kody:@kentcdodds/cursor/request' export default async function main() { return await cursorRequest({ path: '/v1/models' }) } ``` ## Pull requests - `createAgent` always enables ManagePullRequest tools. If you create via raw `cursorRequest`, send `autoCreatePR: true` so those tools are available. - When the cloud agent's task results in code changes, **the agent itself must** push its branch and create/update the PR via Cursor Cloud's ManagePullRequest before finishing. Do **not** have Kody, a Kody workflow, or Kody's GitHub integration open the PR as a substitute. - The PR must be authored by the Kent C. Dodds Cursor/GitHub account so the standard AI reviewer is triggered. - Include this PR-authorship requirement explicitly in every agent creation prompt when spawning via this package. ## Edge cases - Prefer this package to launch Cursor Cloud agents. Unless the user requests another model, resolve the current latest Grok id with `listModels` — do not default to Fable or GPT. - `listRepositories` is rate-limited (about 1/min, 30/hour) and may be slow. - Workers (`./workers`) need a pool service-account key path Cursor exposes; ordinary Cloud Agent flows do not. - Enterprise Admin / Analytics / AI-Code-Tracking / Bugbot: use `./request`, not typed helpers. - Never paste `cursorApiKey` or GitHub token values into chat or logs. - File handoffs: use `@kentcdodds/agent-files` `createUpload` → PUT → put `downloadUrl` in prompt text. Do not treat `prompt.images` as VM bytes.