@react-grab/cursor 0.0.55 → 0.0.57

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/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @react-grab/cursor
2
+
3
+ Cursor agent provider for React Grab. Requires running a local server that interfaces with the Cursor Agent CLI.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @react-grab/cursor
9
+ # or
10
+ pnpm add @react-grab/cursor
11
+ # or
12
+ bun add @react-grab/cursor
13
+ # or
14
+ yarn add @react-grab/cursor
15
+ ```
16
+
17
+ ## Server Setup
18
+
19
+ The server runs on port `5567` by default.
20
+
21
+ ### Vite
22
+
23
+ ```ts
24
+ // vite.config.ts
25
+ import "@react-grab/cursor/server";
26
+ ```
27
+
28
+ ### Next.js
29
+
30
+ ```ts
31
+ // next.config.ts
32
+ import "@react-grab/cursor/server";
33
+ ```
34
+
35
+ ## Client Usage
36
+
37
+ ```tsx
38
+ import { init } from "react-grab/core";
39
+ import { createCursorAgentProvider } from "@react-grab/cursor/client";
40
+
41
+ const agentProvider = createCursorAgentProvider();
42
+
43
+ init({
44
+ agent: {
45
+ provider: agentProvider,
46
+ },
47
+ });
48
+ ```
49
+
50
+ ## Configuration
51
+
52
+ You can customize the server URL and default options:
53
+
54
+ ```tsx
55
+ const agentProvider = createCursorAgentProvider({
56
+ getOptions: () => ({
57
+ model: "composer-1",
58
+ workspace: "/path/to/workspace",
59
+ }),
60
+ });
61
+ ```
62
+
63
+ ## How It Works
64
+
65
+ ```
66
+ ┌─────────────────┐ HTTP ┌─────────────────┐ stdin ┌─────────────────┐
67
+ │ │ localhost:5567 │ │ │ │
68
+ │ React Grab │ ──────────────► │ Server │ ─────────────► │ cursor-agent │
69
+ │ (Browser) │ ◄────────────── │ (Node.js) │ ◄───────────── │ (CLI) │
70
+ │ │ SSE │ │ stdout │ │
71
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
72
+ Client Server Agent
73
+ ```
74
+
75
+ 1. **React Grab** sends the selected element context to the server via HTTP POST
76
+ 2. **Server** receives the request and spawns the `cursor-agent` CLI process
77
+ 3. **cursor-agent** processes the request and streams JSON responses to stdout
78
+ 4. **Server** relays status updates to the client via Server-Sent Events (SSE)
package/dist/client.cjs CHANGED
@@ -60,7 +60,10 @@ var createCursorAgentProvider = (providerOptions = {}) => {
60
60
  });
61
61
  return {
62
62
  send: async function* (context, signal) {
63
- const mergedContext = { ...context, options: mergeOptions(context.options) };
63
+ const mergedContext = {
64
+ ...context,
65
+ options: mergeOptions(context.options)
66
+ };
64
67
  yield* streamFromServer(serverUrl, mergedContext, signal);
65
68
  },
66
69
  resume: async function* (sessionId, signal) {
@@ -74,14 +77,33 @@ var createCursorAgentProvider = (providerOptions = {}) => {
74
77
  throw new Error(`Session ${sessionId} not found`);
75
78
  }
76
79
  const context = session.context;
77
- const mergedContext = { ...context, options: mergeOptions(context.options) };
80
+ const mergedContext = {
81
+ ...context,
82
+ options: mergeOptions(context.options)
83
+ };
78
84
  yield "Resuming...";
79
85
  yield* streamFromServer(serverUrl, mergedContext, signal);
80
86
  },
81
87
  supportsResume: true
82
88
  };
83
89
  };
84
- var defaultCursorAgentProvider = createCursorAgentProvider();
90
+ var attachAgent = async () => {
91
+ if (typeof window === "undefined") return;
92
+ const provider = createCursorAgentProvider();
93
+ const api = window.__REACT_GRAB__;
94
+ if (api) {
95
+ api.setAgent({ provider });
96
+ return;
97
+ }
98
+ window.addEventListener(
99
+ "react-grab:init",
100
+ (event) => {
101
+ const customEvent = event;
102
+ customEvent.detail.setAgent({ provider });
103
+ },
104
+ { once: true }
105
+ );
106
+ };
85
107
 
108
+ exports.attachAgent = attachAgent;
86
109
  exports.createCursorAgentProvider = createCursorAgentProvider;
87
- exports.defaultCursorAgentProvider = defaultCursorAgentProvider;
package/dist/client.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AgentProvider } from 'react-grab/core';
1
+ import { init, AgentProvider } from 'react-grab/core';
2
2
 
3
3
  interface CursorAgentOptions {
4
4
  model?: string;
@@ -9,6 +9,11 @@ interface CursorAgentProviderOptions {
9
9
  getOptions?: () => Partial<CursorAgentOptions>;
10
10
  }
11
11
  declare const createCursorAgentProvider: (providerOptions?: CursorAgentProviderOptions) => AgentProvider<CursorAgentOptions>;
12
- declare const defaultCursorAgentProvider: AgentProvider<CursorAgentOptions>;
12
+ declare global {
13
+ interface Window {
14
+ __REACT_GRAB__?: ReturnType<typeof init>;
15
+ }
16
+ }
17
+ declare const attachAgent: () => Promise<void>;
13
18
 
14
- export { createCursorAgentProvider, defaultCursorAgentProvider };
19
+ export { attachAgent, createCursorAgentProvider };
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AgentProvider } from 'react-grab/core';
1
+ import { init, AgentProvider } from 'react-grab/core';
2
2
 
3
3
  interface CursorAgentOptions {
4
4
  model?: string;
@@ -9,6 +9,11 @@ interface CursorAgentProviderOptions {
9
9
  getOptions?: () => Partial<CursorAgentOptions>;
10
10
  }
11
11
  declare const createCursorAgentProvider: (providerOptions?: CursorAgentProviderOptions) => AgentProvider<CursorAgentOptions>;
12
- declare const defaultCursorAgentProvider: AgentProvider<CursorAgentOptions>;
12
+ declare global {
13
+ interface Window {
14
+ __REACT_GRAB__?: ReturnType<typeof init>;
15
+ }
16
+ }
17
+ declare const attachAgent: () => Promise<void>;
13
18
 
14
- export { createCursorAgentProvider, defaultCursorAgentProvider };
19
+ export { attachAgent, createCursorAgentProvider };
package/dist/client.js CHANGED
@@ -58,7 +58,10 @@ var createCursorAgentProvider = (providerOptions = {}) => {
58
58
  });
59
59
  return {
60
60
  send: async function* (context, signal) {
61
- const mergedContext = { ...context, options: mergeOptions(context.options) };
61
+ const mergedContext = {
62
+ ...context,
63
+ options: mergeOptions(context.options)
64
+ };
62
65
  yield* streamFromServer(serverUrl, mergedContext, signal);
63
66
  },
64
67
  resume: async function* (sessionId, signal) {
@@ -72,13 +75,32 @@ var createCursorAgentProvider = (providerOptions = {}) => {
72
75
  throw new Error(`Session ${sessionId} not found`);
73
76
  }
74
77
  const context = session.context;
75
- const mergedContext = { ...context, options: mergeOptions(context.options) };
78
+ const mergedContext = {
79
+ ...context,
80
+ options: mergeOptions(context.options)
81
+ };
76
82
  yield "Resuming...";
77
83
  yield* streamFromServer(serverUrl, mergedContext, signal);
78
84
  },
79
85
  supportsResume: true
80
86
  };
81
87
  };
82
- var defaultCursorAgentProvider = createCursorAgentProvider();
88
+ var attachAgent = async () => {
89
+ if (typeof window === "undefined") return;
90
+ const provider = createCursorAgentProvider();
91
+ const api = window.__REACT_GRAB__;
92
+ if (api) {
93
+ api.setAgent({ provider });
94
+ return;
95
+ }
96
+ window.addEventListener(
97
+ "react-grab:init",
98
+ (event) => {
99
+ const customEvent = event;
100
+ customEvent.detail.setAgent({ provider });
101
+ },
102
+ { once: true }
103
+ );
104
+ };
83
105
 
84
- export { createCursorAgentProvider, defaultCursorAgentProvider };
106
+ export { attachAgent, createCursorAgentProvider };
package/dist/server.cjs CHANGED
@@ -739,14 +739,14 @@ var Hono = class {
739
739
  }
740
740
  #notFoundHandler = notFoundHandler;
741
741
  errorHandler = errorHandler;
742
- route(path, app2) {
742
+ route(path, app) {
743
743
  const subApp = this.basePath(path);
744
- app2.routes.map((r) => {
744
+ app.routes.map((r) => {
745
745
  let handler;
746
- if (app2.errorHandler === errorHandler) {
746
+ if (app.errorHandler === errorHandler) {
747
747
  handler = r.handler;
748
748
  } else {
749
- handler = async (c, next) => (await compose([], app2.errorHandler)(c, () => r.handler(c, next))).res;
749
+ handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
750
750
  handler[COMPOSED_HANDLER] = r.handler;
751
751
  }
752
752
  subApp.#addRoute(r.method, r.path, handler);
@@ -2253,7 +2253,7 @@ var createAdaptorServer = (options) => {
2253
2253
  };
2254
2254
  var serve = (options, listeningListener) => {
2255
2255
  const server = createAdaptorServer(options);
2256
- server.listen(options?.port, options.hostname, () => {
2256
+ server.listen(options?.port ?? 3e3, options.hostname, () => {
2257
2257
  server.address();
2258
2258
  });
2259
2259
  return server;
@@ -2277,9 +2277,9 @@ var extractTextFromMessage = (message) => {
2277
2277
  return message.content.filter((block) => block.type === "text").map((block) => block.text).join(" ").trim();
2278
2278
  };
2279
2279
  var createServer = () => {
2280
- const app2 = new Hono2();
2281
- app2.use("/*", cors());
2282
- app2.post("/agent", async (context) => {
2280
+ const app = new Hono2();
2281
+ app.use("/*", cors());
2282
+ app.post("/agent", async (context) => {
2283
2283
  const body = await context.req.json();
2284
2284
  const { content, prompt, options } = body;
2285
2285
  const fullPrompt = `${prompt}
@@ -2301,7 +2301,7 @@ ${content}`;
2301
2301
  cursorAgentArgs.push("--workspace", process.cwd());
2302
2302
  }
2303
2303
  try {
2304
- await stream2.writeSSE({ data: "Starting Cursor Agent...", event: "status" });
2304
+ await stream2.writeSSE({ data: "Planning next moves", event: "status" });
2305
2305
  const cursorProcess = child_process.spawn("cursor-agent", cursorAgentArgs, {
2306
2306
  stdio: ["pipe", "pipe", "pipe"],
2307
2307
  env: { ...process.env }
@@ -2313,29 +2313,43 @@ ${content}`;
2313
2313
  switch (event.type) {
2314
2314
  case "system":
2315
2315
  if (event.subtype === "init") {
2316
- await stream2.writeSSE({ data: "Connected to Cursor Agent", event: "status" });
2316
+ await stream2.writeSSE({
2317
+ data: "Planning next moves",
2318
+ event: "status"
2319
+ });
2317
2320
  }
2318
2321
  break;
2319
2322
  case "thinking":
2320
2323
  if (event.subtype === "completed") {
2321
- await stream2.writeSSE({ data: "Processing...", event: "status" });
2324
+ await stream2.writeSSE({
2325
+ data: "Thinking\u2026",
2326
+ event: "status"
2327
+ });
2322
2328
  }
2323
2329
  break;
2324
2330
  case "assistant": {
2325
- const text = extractTextFromMessage(event.message);
2326
- if (text) {
2327
- const statusUpdate = text.length > 150 ? `${text.slice(0, 150)}...` : text;
2328
- await stream2.writeSSE({ data: statusUpdate, event: "status" });
2331
+ const textContent = extractTextFromMessage(event.message);
2332
+ if (textContent) {
2333
+ await stream2.writeSSE({ data: textContent, event: "status" });
2329
2334
  }
2330
2335
  break;
2331
2336
  }
2332
2337
  case "result":
2333
2338
  if (event.subtype === "success") {
2334
- await stream2.writeSSE({ data: "Completed successfully", event: "status" });
2339
+ await stream2.writeSSE({
2340
+ data: "Completed successfully",
2341
+ event: "status"
2342
+ });
2335
2343
  } else if (event.subtype === "error" || event.is_error) {
2336
- await stream2.writeSSE({ data: `Error: ${event.result || "Unknown error"}`, event: "error" });
2344
+ await stream2.writeSSE({
2345
+ data: `Error: ${event.result || "Unknown error"}`,
2346
+ event: "error"
2347
+ });
2337
2348
  } else {
2338
- await stream2.writeSSE({ data: "Task finished", event: "status" });
2349
+ await stream2.writeSSE({
2350
+ data: "Task finished",
2351
+ event: "status"
2352
+ });
2339
2353
  }
2340
2354
  break;
2341
2355
  }
@@ -2372,21 +2386,24 @@ ${content}`;
2372
2386
  await stream2.writeSSE({ data: "", event: "done" });
2373
2387
  } catch (error) {
2374
2388
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
2375
- await stream2.writeSSE({ data: `Error: ${errorMessage}`, event: "error" });
2389
+ await stream2.writeSSE({
2390
+ data: `Error: ${errorMessage}`,
2391
+ event: "error"
2392
+ });
2376
2393
  await stream2.writeSSE({ data: "", event: "done" });
2377
2394
  }
2378
2395
  });
2379
2396
  });
2380
- app2.get("/health", (context) => {
2397
+ app.get("/health", (context) => {
2381
2398
  return context.json({ status: "ok", provider: "cursor" });
2382
2399
  });
2383
- return app2;
2400
+ return app;
2401
+ };
2402
+ var startServer = (port = DEFAULT_PORT) => {
2403
+ const app = createServer();
2404
+ serve({ fetch: app.fetch, port });
2405
+ console.log("React Grab Cursor server running on port", port);
2384
2406
  };
2385
- var app = createServer();
2386
- serve({
2387
- fetch: app.fetch,
2388
- port: DEFAULT_PORT
2389
- });
2390
- console.log("React Grab Cursor server running on port", DEFAULT_PORT);
2391
2407
 
2392
2408
  exports.createServer = createServer;
2409
+ exports.startServer = startServer;
package/dist/server.d.cts CHANGED
@@ -2,5 +2,6 @@ import * as hono_types from 'hono/types';
2
2
  import { Hono } from 'hono';
3
3
 
4
4
  declare const createServer: () => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
5
+ declare const startServer: (port?: number) => void;
5
6
 
6
- export { createServer };
7
+ export { createServer, startServer };
package/dist/server.d.ts CHANGED
@@ -2,5 +2,6 @@ import * as hono_types from 'hono/types';
2
2
  import { Hono } from 'hono';
3
3
 
4
4
  declare const createServer: () => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
5
+ declare const startServer: (port?: number) => void;
5
6
 
6
- export { createServer };
7
+ export { createServer, startServer };
package/dist/server.js CHANGED
@@ -733,14 +733,14 @@ var Hono = class {
733
733
  }
734
734
  #notFoundHandler = notFoundHandler;
735
735
  errorHandler = errorHandler;
736
- route(path, app2) {
736
+ route(path, app) {
737
737
  const subApp = this.basePath(path);
738
- app2.routes.map((r) => {
738
+ app.routes.map((r) => {
739
739
  let handler;
740
- if (app2.errorHandler === errorHandler) {
740
+ if (app.errorHandler === errorHandler) {
741
741
  handler = r.handler;
742
742
  } else {
743
- handler = async (c, next) => (await compose([], app2.errorHandler)(c, () => r.handler(c, next))).res;
743
+ handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
744
744
  handler[COMPOSED_HANDLER] = r.handler;
745
745
  }
746
746
  subApp.#addRoute(r.method, r.path, handler);
@@ -2247,7 +2247,7 @@ var createAdaptorServer = (options) => {
2247
2247
  };
2248
2248
  var serve = (options, listeningListener) => {
2249
2249
  const server = createAdaptorServer(options);
2250
- server.listen(options?.port, options.hostname, () => {
2250
+ server.listen(options?.port ?? 3e3, options.hostname, () => {
2251
2251
  server.address();
2252
2252
  });
2253
2253
  return server;
@@ -2271,9 +2271,9 @@ var extractTextFromMessage = (message) => {
2271
2271
  return message.content.filter((block) => block.type === "text").map((block) => block.text).join(" ").trim();
2272
2272
  };
2273
2273
  var createServer = () => {
2274
- const app2 = new Hono2();
2275
- app2.use("/*", cors());
2276
- app2.post("/agent", async (context) => {
2274
+ const app = new Hono2();
2275
+ app.use("/*", cors());
2276
+ app.post("/agent", async (context) => {
2277
2277
  const body = await context.req.json();
2278
2278
  const { content, prompt, options } = body;
2279
2279
  const fullPrompt = `${prompt}
@@ -2295,7 +2295,7 @@ ${content}`;
2295
2295
  cursorAgentArgs.push("--workspace", process.cwd());
2296
2296
  }
2297
2297
  try {
2298
- await stream2.writeSSE({ data: "Starting Cursor Agent...", event: "status" });
2298
+ await stream2.writeSSE({ data: "Planning next moves", event: "status" });
2299
2299
  const cursorProcess = spawn("cursor-agent", cursorAgentArgs, {
2300
2300
  stdio: ["pipe", "pipe", "pipe"],
2301
2301
  env: { ...process.env }
@@ -2307,29 +2307,43 @@ ${content}`;
2307
2307
  switch (event.type) {
2308
2308
  case "system":
2309
2309
  if (event.subtype === "init") {
2310
- await stream2.writeSSE({ data: "Connected to Cursor Agent", event: "status" });
2310
+ await stream2.writeSSE({
2311
+ data: "Planning next moves",
2312
+ event: "status"
2313
+ });
2311
2314
  }
2312
2315
  break;
2313
2316
  case "thinking":
2314
2317
  if (event.subtype === "completed") {
2315
- await stream2.writeSSE({ data: "Processing...", event: "status" });
2318
+ await stream2.writeSSE({
2319
+ data: "Thinking\u2026",
2320
+ event: "status"
2321
+ });
2316
2322
  }
2317
2323
  break;
2318
2324
  case "assistant": {
2319
- const text = extractTextFromMessage(event.message);
2320
- if (text) {
2321
- const statusUpdate = text.length > 150 ? `${text.slice(0, 150)}...` : text;
2322
- await stream2.writeSSE({ data: statusUpdate, event: "status" });
2325
+ const textContent = extractTextFromMessage(event.message);
2326
+ if (textContent) {
2327
+ await stream2.writeSSE({ data: textContent, event: "status" });
2323
2328
  }
2324
2329
  break;
2325
2330
  }
2326
2331
  case "result":
2327
2332
  if (event.subtype === "success") {
2328
- await stream2.writeSSE({ data: "Completed successfully", event: "status" });
2333
+ await stream2.writeSSE({
2334
+ data: "Completed successfully",
2335
+ event: "status"
2336
+ });
2329
2337
  } else if (event.subtype === "error" || event.is_error) {
2330
- await stream2.writeSSE({ data: `Error: ${event.result || "Unknown error"}`, event: "error" });
2338
+ await stream2.writeSSE({
2339
+ data: `Error: ${event.result || "Unknown error"}`,
2340
+ event: "error"
2341
+ });
2331
2342
  } else {
2332
- await stream2.writeSSE({ data: "Task finished", event: "status" });
2343
+ await stream2.writeSSE({
2344
+ data: "Task finished",
2345
+ event: "status"
2346
+ });
2333
2347
  }
2334
2348
  break;
2335
2349
  }
@@ -2366,21 +2380,23 @@ ${content}`;
2366
2380
  await stream2.writeSSE({ data: "", event: "done" });
2367
2381
  } catch (error) {
2368
2382
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
2369
- await stream2.writeSSE({ data: `Error: ${errorMessage}`, event: "error" });
2383
+ await stream2.writeSSE({
2384
+ data: `Error: ${errorMessage}`,
2385
+ event: "error"
2386
+ });
2370
2387
  await stream2.writeSSE({ data: "", event: "done" });
2371
2388
  }
2372
2389
  });
2373
2390
  });
2374
- app2.get("/health", (context) => {
2391
+ app.get("/health", (context) => {
2375
2392
  return context.json({ status: "ok", provider: "cursor" });
2376
2393
  });
2377
- return app2;
2394
+ return app;
2395
+ };
2396
+ var startServer = (port = DEFAULT_PORT) => {
2397
+ const app = createServer();
2398
+ serve({ fetch: app.fetch, port });
2399
+ console.log("React Grab Cursor server running on port", port);
2378
2400
  };
2379
- var app = createServer();
2380
- serve({
2381
- fetch: app.fetch,
2382
- port: DEFAULT_PORT
2383
- });
2384
- console.log("React Grab Cursor server running on port", DEFAULT_PORT);
2385
2401
 
2386
- export { createServer };
2402
+ export { createServer, startServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-grab/cursor",
3
- "version": "0.0.55",
3
+ "version": "0.0.57",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./client": {
@@ -23,7 +23,7 @@
23
23
  "dependencies": {
24
24
  "@hono/node-server": "^1.19.6",
25
25
  "hono": "^4.0.0",
26
- "react-grab": "0.0.55"
26
+ "react-grab": "0.0.57"
27
27
  },
28
28
  "scripts": {
29
29
  "dev": "tsup --watch",