effect-cursor-sdk 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DEPRECATIONS.md CHANGED
@@ -1,88 +1,5 @@
1
1
  # Deprecations
2
2
 
3
- This document lists deprecated `effect-cursor-sdk` APIs, what to use instead today, and how names are expected to change in the **next major** release. Deprecated APIs remain available until that major; follow [CHANGELOG.md](./CHANGELOG.md) and release notes when upgrading.
3
+ There are currently **no deprecations** in this version of `effect-cursor-sdk`.
4
4
 
5
- ## Status definitions
6
-
7
- | Status | Meaning |
8
- | ---------------------- | ---------------------------------------------------------------------------------------------------- |
9
- | **Deprecated** | Still supported; avoid in new code. May show IDE warnings via TSDoc `@deprecated`. |
10
- | **Preferred now** | Current recommended API; use with [`loadCursorConfig`](./README.md#quick-start) and related helpers. |
11
- | **Planned next major** | Intended replacement names after deprecated overloads and `*FromConfig` suffixes are removed. |
12
-
13
- ## Agent entry: plain `AgentOptions` vs config-first
14
-
15
- Passing raw [`AgentOptions`](https://cursor.com/docs/sdk/typescript) (including a plain `apiKey` string) directly to `CursorAgentService` agent entry points is **deprecated**. Prefer loading `CursorConfig` with `loadCursorConfig` (exported from this package) and using the `*FromConfig` methods so secrets stay [`Redacted`](https://effect.website/docs/schema/redacted/) until merged for the SDK boundary.
16
-
17
- ### API mapping
18
-
19
- | Deprecated (`CursorAgentService`) | Preferred now | Planned next major (same signatures as “Preferred now”) |
20
- | --------------------------------- | ----------------------------------------------- | ------------------------------------------------------- |
21
- | `create(options)` | `createFromConfig(config, overrides?)` | `create(config, overrides?)` |
22
- | `resume(agentId, options?)` | `resumeFromConfig(agentId, config, overrides?)` | `resume(agentId, config, overrides?)` |
23
- | `prompt(message, options?)` | `promptFromConfig(message, config, overrides?)` | `prompt(message, config, overrides?)` |
24
- | `scoped(options)` | `scopedFromConfig(config, overrides?)` | `scoped(config, overrides?)` |
25
-
26
- The `*FromConfig` suffixes exist today to keep deprecated plain-`AgentOptions` entry points without breaking callers. After removal of the legacy forms, the shorter names above are the intended stable surface.
27
-
28
- ### Migrate `create`
29
-
30
- **Before (deprecated):**
31
-
32
- ```ts
33
- const agent =
34
- yield *
35
- agents.create({
36
- apiKey: process.env.CURSOR_API_KEY,
37
- model: { id: "composer-2" },
38
- local: { cwd: process.cwd() },
39
- });
40
- ```
41
-
42
- **After (preferred):**
43
-
44
- ```ts
45
- const config = yield * loadCursorConfig;
46
- const agent =
47
- yield *
48
- agents.createFromConfig(config, {
49
- model: { id: "composer-2" },
50
- local: { cwd: process.cwd() },
51
- });
52
- ```
53
-
54
- If you need full control over merging into SDK options, call `agentOptionsFromConfig(config, overrides)` and pass the result only through internal or transitional code paths; application code should still prefer `createFromConfig`.
55
-
56
- ### Migrate `prompt`
57
-
58
- **Before (deprecated):**
59
-
60
- ```ts
61
- const result =
62
- yield *
63
- agents.prompt("Summarize the README", {
64
- apiKey: process.env.CURSOR_API_KEY,
65
- model: { id: "composer-2" },
66
- });
67
- ```
68
-
69
- **After (preferred):**
70
-
71
- ```ts
72
- const config = yield * loadCursorConfig;
73
- const result =
74
- yield *
75
- agents.promptFromConfig("Summarize the README", config, {
76
- model: { id: "composer-2" },
77
- });
78
- ```
79
-
80
- ## Other deprecations
81
-
82
- `CursorSdkFactory` exposes raw SDK-style `create` / `resume` / `prompt` helpers for tests and advanced wiring; those are **deprecated for application code** in favor of `CursorAgentService` with the config-first flow above. See TSDoc on `CursorSdkFactory` in the published types.
83
-
84
- ## Where else this is documented
85
-
86
- - [README.md](./README.md) — quick summary and link here.
87
- - Package root ships this file next to `README.md` on npm (see `package.json` `files`).
88
- - Per-symbol `@deprecated` tags on `CursorAgentService` and related exports in the TypeScript declarations.
5
+ For historical breaking changes and release notes, see [CHANGELOG.md](./CHANGELOG.md).
package/README.md CHANGED
@@ -6,8 +6,7 @@ Effect-native access to the new [Cursor SDK](https://cursor.com/docs/sdk/typescr
6
6
 
7
7
  `effect-cursor-sdk` wraps `@cursor/sdk` with Effect services, layers, scoped resource management, tagged errors, observability hooks, deterministic mocks, and ready-made runtimes. The upstream SDK remains the source of truth for Cursor-owned types; this package adds Effect ergonomics without creating a parallel model that can drift.
8
8
 
9
- > [!WARNING]
10
- > This project is in early development. While all functionality is available, there is still much room for improvement. Contributions are welcome!
9
+ If you want to build with Cursor agents, and you are using Effect, this package is for you.
11
10
 
12
11
  ## Philosophy
13
12
 
@@ -23,7 +22,6 @@ Effect-native access to the new [Cursor SDK](https://cursor.com/docs/sdk/typescr
23
22
  - [SDK coverage & compatibility](./docs/SDK_COVERAGE.md) — wrapper checklist, audit script, release alignment
24
23
  - [Recipes](./docs/RECIPES.md) — short patterns (config-first agent, streaming, pagination, lifecycle, tests)
25
24
  - [Release checklist](./docs/RELEASE_CHECKLIST.md) — gates, SDK bumps, Changesets
26
- - [Next major migration (planned)](./docs/MIGRATION_NEXT_MAJOR.md) — config-first renames after deprecations are removed
27
25
 
28
26
  ## Feature Coverage
29
27
 
@@ -36,15 +34,43 @@ Effect-native access to the new [Cursor SDK](https://cursor.com/docs/sdk/typescr
36
34
  | `Agent.list`, `get`, `listRuns`, `getRun`, messages | `CursorInspectionService` |
37
35
  | `Agent.archive`, `unarchive`, `delete` | `CursorInspectionService` |
38
36
  | `Cursor.me`, models, repositories | `CursorInspectionService` |
39
- | MCP servers, sub-agents, local/cloud options, model options | Defaults via `CursorConfig` / `loadCursorConfig`; merged SDK `AgentOptions` ([deprecated](./DEPRECATIONS.md) at agent entry) |
37
+ | MCP servers, sub-agents, local/cloud options, model options | Defaults via `CursorConfig` / `loadCursorConfig`; merged into SDK `AgentOptions` at the boundary |
40
38
  | Local run event helpers and platform helpers | Re-exported from `@cursor/sdk` |
41
39
 
40
+ ## Use cases
41
+
42
+ If you are new to the Cursor SDK, take a look at the [Cursor SDK Cookbook](https://github.com/cursor/cookbook). This cookbook provides some (cool!) examples of what you could do with the SDK.
43
+
44
+ `effect-cursor-sdk` even [uses itself](#automated-changeset-agent) to spin up a Cursor agent to create changesets for pull requests against `main`! 🤯
45
+
42
46
  ## Install
43
47
 
44
48
  ```bash
45
49
  bun add effect-cursor-sdk effect @cursor/sdk
46
50
  ```
47
51
 
52
+ `effect` is a **peer dependency** (Effect v4). `@cursor/sdk` is bundled with this package, but installing it in your app keeps SDK-owned types and versions explicit when you import from `@cursor/sdk` alongside the wrapper.
53
+
54
+ ### Requirements
55
+
56
+ - **Runtime:** Bun or Node.js (examples and CI use Bun; Node works for library consumers).
57
+ - **Effect:** `^4.0.0-beta` (see [Effect v4](https://effect.website/docs/introduction/overview) — still pre-release on the Effect side).
58
+ - **Cursor SDK:** `@cursor/sdk` `^1.0.x` (pinned in this repo; see [SDK coverage](./docs/SDK_COVERAGE.md) when upgrading).
59
+
60
+ ### Configuration
61
+
62
+ `loadCursorConfig` reads optional environment variables through Effect’s `ConfigProvider` (by default, `process.env`):
63
+
64
+ | Variable | Purpose |
65
+ | --- | --- |
66
+ | `CURSOR_API_KEY` | API key for Cursor (stored as `Redacted` until merged into SDK options). |
67
+ | `CURSOR_MODEL` | Default model id (for example `composer-2`). |
68
+ | `CURSOR_LOCAL_CWD` | Default working directory for local agents. |
69
+
70
+ All fields are optional at load time; missing `CURSOR_API_KEY` logs a warning and later SDK calls fail with authentication errors unless you pass overrides. Per-call overrides still win when using `create`, `scoped`, `prompt`, and `resume`.
71
+
72
+ For offline tests and CI, use [`mockLayer`](#mocks-and-tests) or `makeMockRuntime` — no API key required.
73
+
48
74
  For development in this repo:
49
75
 
50
76
  ```bash
@@ -60,7 +86,7 @@ minimal first script to production-style Effect composition:
60
86
 
61
87
  | Example | What it demonstrates |
62
88
  | --- | --- |
63
- | [`quickstart`](./examples/quickstart) | First config-first local agent call with `loadCursorConfig`, `agentOptionsFromConfig`, and `collectText`. |
89
+ | [`quickstart`](./examples/quickstart) | First config-first local agent call with `loadCursorConfig`, `scoped`, and `collectText`. |
64
90
  | [`cli`](./examples/cli) | A small terminal app with `liveRuntime`, offline `makeMockRuntime`, CLI overrides, and tagged error handling. |
65
91
  | [`basic-agent-workflow`](./examples/basic-agent-workflow) | Scoped agents, run status listeners, streaming, capability checks, and artifact listing/downloads. |
66
92
  | [`advanced-ops-dashboard`](./examples/advanced-ops-dashboard) | Inspection APIs, confirmation-gated lifecycle operations, parallel Effect composition, retries/timeouts, telemetry, redaction, and rich mocks. |
@@ -73,7 +99,7 @@ bun run examples:typecheck
73
99
 
74
100
  ## Quick Start
75
101
 
76
- Load environment defaults with `loadCursorConfig`, then create agents with `createFromConfig` (and `scopedFromConfig`, `promptFromConfig`, `resumeFromConfig` as needed). The API key stays in `Redacted` form until the merge step; `AgentOptions.apiKey` remains a plain string at the SDK boundary.
102
+ Load environment defaults with `loadCursorConfig`, then create agents with `create` (and `scoped`, `prompt`, `resume` as needed). The API key stays in `Redacted` form until the merge step; `AgentOptions.apiKey` remains a plain string at the SDK boundary.
77
103
 
78
104
  ```ts
79
105
  import {
@@ -89,7 +115,7 @@ const program = Effect.gen(function* () {
89
115
  const runs = yield* CursorRunService;
90
116
 
91
117
  const config = yield* loadCursorConfig;
92
- const agent = yield* agents.createFromConfig(config, {
118
+ const agent = yield* agents.create(config, {
93
119
  // Override the given config optionally with custom values
94
120
  model: { id: "composer-2" },
95
121
  local: { cwd: process.cwd() },
@@ -105,38 +131,11 @@ const program = Effect.gen(function* () {
105
131
 
106
132
  Effect’s default `ConfigProvider` reads `process.env`, so you usually do not need to install a custom provider for this.
107
133
 
108
- If you need full control over the merge into SDK options, you can still call `agentOptionsFromConfig` yourself and pass the result to deprecated `create`; prefer `createFromConfig` in application code.
109
-
110
- ## Plain `AgentOptions` at the agent boundary (deprecated)
111
-
112
- Passing raw `AgentOptions` (for example `apiKey: process.env.CURSOR_API_KEY`) to `create`, `resume`, `prompt`, or `scoped` is **deprecated**. It still works for compatibility, but prefer the config flow above.
113
-
114
- ```ts
115
- import { CursorAgentService, CursorRunService, liveLayer } from "effect-cursor-sdk";
116
- import { Effect } from "effect";
117
-
118
- const legacyProgram = Effect.gen(function* () {
119
- const agents = yield* CursorAgentService;
120
- const runs = yield* CursorRunService;
121
-
122
- const agent = yield* agents.create({
123
- apiKey: process.env.CURSOR_API_KEY,
124
- model: { id: "composer-2" },
125
- local: { cwd: process.cwd() },
126
- });
127
-
128
- const run = yield* agents.send(agent, "Explain this repository");
129
- const text = yield* runs.collectText(run);
130
- yield* agents.dispose(agent);
131
- return text;
132
- }).pipe(Effect.provide(liveLayer));
133
- ```
134
-
135
- Migrate by replacing `agents.create({ ... })` with `const config = yield* loadCursorConfig` and `agents.createFromConfig(config, { ... })` (or the other `*FromConfig` helpers).
134
+ For advanced SDK-factory wiring in tests, you can still call `agentOptionsFromConfig` to merge config into plain `AgentOptions` before passing them to `CursorSdkFactory`.
136
135
 
137
136
  ## Scoped Agents
138
137
 
139
- Prefer `scopedFromConfig` when an agent should be disposed automatically:
138
+ Prefer `scoped` when an agent should be disposed automatically:
140
139
 
141
140
  ```ts
142
141
  import { CursorAgentService, loadCursorConfig, liveLayer } from "effect-cursor-sdk";
@@ -146,7 +145,7 @@ const program = Effect.scoped(
146
145
  Effect.gen(function* () {
147
146
  const agents = yield* CursorAgentService;
148
147
  const config = yield* loadCursorConfig;
149
- const agent = yield* agents.scopedFromConfig(config, {
148
+ const agent = yield* agents.scoped(config, {
150
149
  model: { id: "composer-2" },
151
150
  local: { cwd: process.cwd() },
152
151
  });
@@ -156,13 +155,24 @@ const program = Effect.scoped(
156
155
  ).pipe(Effect.provide(liveLayer));
157
156
  ```
158
157
 
158
+ ## Resume an existing agent
159
+
160
+ Use `resume` with the agent id from a prior run or from `CursorInspectionService.listAgents`:
161
+
162
+ ```ts
163
+ const config = yield* loadCursorConfig;
164
+ const agent = yield* agents.resume("bc_abc123", config, {
165
+ local: { cwd: process.cwd() },
166
+ });
167
+ ```
168
+
159
169
  ## Cloud Agents
160
170
 
161
171
  Cloud options are merged as SDK overrides on top of loaded config:
162
172
 
163
173
  ```ts
164
174
  const config = yield* loadCursorConfig;
165
- const agent = yield* agents.createFromConfig(config, {
175
+ const agent = yield* agents.create(config, {
166
176
  model: { id: "composer-2" },
167
177
  cloud: {
168
178
  repos: [
@@ -198,6 +208,10 @@ yield* runs.streamEvents(run).pipe(
198
208
  );
199
209
  ```
200
210
 
211
+ ## Artifacts
212
+
213
+ List and download run outputs with `CursorArtifactService` after `send` completes. See the [Artifacts recipe](./docs/RECIPES.md#artifacts) for path resolution and download patterns.
214
+
201
215
  ## Inspection And Metadata
202
216
 
203
217
  Use `CursorInspectionService` for agent/run listings, messages, lifecycle operations, account metadata, model discovery, and connected repositories.
@@ -214,7 +228,7 @@ const repos = yield* inspection.listRepositories();
214
228
 
215
229
  Because every Cursor call is an `Effect`, you compose it like the rest of your program: parallel requests, timeouts, retries, logging, and layers all work the same way.
216
230
 
217
- This agent garden snapshot loads your catalog in parallel, adds a resilient boundary around the batch, logs a safe summary (counts and IDs only — never log API keys), then asks Cursor for a one-shot triage opinion via `promptFromConfig`:
231
+ This agent garden snapshot loads your catalog in parallel, adds a resilient boundary around the batch, logs a safe summary (counts and IDs only — never log API keys), then asks Cursor for a one-shot triage opinion via `prompt`:
218
232
 
219
233
  ```ts
220
234
  import {
@@ -250,7 +264,7 @@ const agentGardenSnapshot = Effect.gen(function* () {
250
264
  repos: catalog.repos.length,
251
265
  });
252
266
 
253
- const triage = yield* agents.promptFromConfig(
267
+ const triage = yield* agents.prompt(
254
268
  [
255
269
  "You are helping on-call. Here is non-secret inventory:",
256
270
  `- Cloud agents (ids): ${catalog.cloud.items.map((a) => a.agentId).join(", ") || "(none)"}`,
@@ -272,10 +286,10 @@ Swap `liveLayer` for `mockLayer({ ... })` in tests and the same program shape ex
272
286
 
273
287
  ## Errors
274
288
 
275
- SDK failures are mapped into tagged errors such as `CursorAuthenticationError`, `CursorRateLimitError`, `CursorConfigurationError`, `CursorNetworkError`, and `CursorUnsupportedOperationError`. The original SDK error is preserved as `cause`, with safe operation context and retryability where available.
289
+ SDK failures are mapped into tagged errors such as `CursorAuthenticationError`, `CursorRateLimitError`, `CursorConfigurationError`, `CursorAgentBusyError`, `CursorNetworkError`, and `CursorUnsupportedOperationError`. The original SDK error is preserved as `cause`, with safe operation context and retryability where available.
276
290
 
277
291
  ```ts
278
- program.pipe(
292
+ const handled = program.pipe(
279
293
  Effect.catchTag("CursorRateLimitError", (error) =>
280
294
  Effect.logWarning(`Cursor rate limited request: ${error.message}`),
281
295
  ),
@@ -302,7 +316,7 @@ import { Effect } from "effect";
302
316
  const testProgram = Effect.gen(function* () {
303
317
  const agents = yield* CursorAgentService;
304
318
  const config = yield* loadCursorConfig;
305
- const agent = yield* agents.createFromConfig(config, { model: { id: "composer-2" } });
319
+ const agent = yield* agents.create(config, { model: { id: "composer-2" } });
306
320
  return yield* agents.send(agent, "Hello");
307
321
  }).pipe(
308
322
  Effect.provide(
@@ -319,11 +333,11 @@ The main exports are:
319
333
 
320
334
  - **Recipes** — common compositions (prompt text, send + collect, pagination, lifecycle guards, artifacts) in [RECIPES.md](./docs/RECIPES.md)
321
335
  - **Observability helpers** (`streamEventsTracked`, `collectTextTracked`, catalog retry/timeout presets, log summaries)
322
- - `CursorAgentService` (prefer `createFromConfig`, `scopedFromConfig`, `promptFromConfig`, `resumeFromConfig` with `loadCursorConfig`; plain `AgentOptions` at the agent boundary is [deprecated](./DEPRECATIONS.md))
336
+ - `CursorAgentService` (`create`, `scoped`, `prompt`, `resume` with `loadCursorConfig`)
323
337
  - `CursorRunService`
324
338
  - `CursorArtifactService`
325
339
  - `CursorInspectionService`
326
- - `CursorSdkFactory` ([deprecated for application code](./DEPRECATIONS.md#other-deprecations); low-level tests and overrides)
340
+ - `CursorSdkFactory` (low-level SDK adapter for tests and advanced overrides)
327
341
  - `liveLayer`, `mockLayer`, `liveRuntime`, `makeMockRuntime`
328
342
  - `CursorConfig`, `cursorConfig`, `agentOptionsFromConfig`, `loadCursorConfig`
329
343
  - tagged Cursor error classes and `mapCursorError`
@@ -350,7 +364,7 @@ Coverage is measured with Vitest v8 coverage. The suite focuses on deterministic
350
364
 
351
365
  ## Deprecations
352
366
 
353
- Whenever you need a single place for what is deprecated, what to use instead, how to migrate, and what may change in the next major (when that is already decided), read **[DEPRECATIONS.md](./DEPRECATIONS.md)**. Pair it with **[CHANGELOG.md](./CHANGELOG.md)** for release-by-release notes; `@deprecated` tags on exported symbols mirror the same intent for day-to-day coding.
367
+ See **[DEPRECATIONS.md](./DEPRECATIONS.md)** for the canonical list of deprecated APIs. Pair it with **[CHANGELOG.md](./CHANGELOG.md)** for release-by-release notes.
354
368
 
355
369
  ## Versioning and Publishing
356
370
 
@@ -368,7 +382,15 @@ User-facing changes should include a Changeset:
368
382
  bun run changeset
369
383
  ```
370
384
 
371
- On `main`, GitHub Actions uses Changesets to open a version PR when pending Changesets exist. After that PR is merged, the same workflow runs `bun run release` and publishes to NPM with the `NPM_TOKEN` repository secret.
385
+ ### Automated Changeset Agent
386
+
387
+ This repository also includes a Cursor-powered changeset agent for pull requests against `main`. The workflow in `.github/workflows/changeset-agent.yml` runs `bun run changeset:agent`, starts a scoped local Cursor SDK agent with this package, asks it to inspect the PR diff, and commits a missing `.changeset/*.md` file back to the PR branch when release impact exists.
388
+
389
+ The job runs only for same-repository, non-draft PRs because it needs both the `CURSOR_API_KEY` repository secret and write access to the PR branch. Forked PRs should add changesets manually or be handled from a trusted maintainer checkout.
390
+
391
+ Other, optional environment variables are `CURSOR_MODEL` for the Cursor model id and `CHANGESET_BASE_REF` for the diff base. See [Changeset Agent](./docs/changeset-agent.md) for the full architecture, prompt contract, security model, and local usage.
392
+
393
+ On `main`, GitHub Actions uses Changesets to open a version PR when pending Changesets exist. After that PR is merged, the same workflow runs `bun run release` and publishes to NPM.
372
394
 
373
395
  For local release preparation, apply pending Changesets and publish only after the package is approved for public release:
374
396