@react-grab/claude-code 0.0.84 → 0.0.86
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/dist/cli.cjs +2 -2
- package/dist/cli.js +2 -2
- package/dist/client.cjs +18 -11
- package/dist/client.d.cts +1 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.global.js +4 -4
- package/dist/client.js +18 -11
- package/dist/server.cjs +64 -6
- package/dist/server.js +64 -6
- package/package.json +4 -3
package/dist/cli.cjs
CHANGED
|
@@ -7138,7 +7138,7 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
7138
7138
|
var DEFAULT_PORT = 4567;
|
|
7139
7139
|
|
|
7140
7140
|
// src/cli.ts
|
|
7141
|
-
var VERSION = "0.0.
|
|
7141
|
+
var VERSION = "0.0.86";
|
|
7142
7142
|
var serverPath = path2.join(__dirname, "server.cjs");
|
|
7143
7143
|
execa(process.execPath, [serverPath], {
|
|
7144
7144
|
detached: true,
|
|
@@ -7146,6 +7146,6 @@ execa(process.execPath, [serverPath], {
|
|
|
7146
7146
|
cleanup: false
|
|
7147
7147
|
}).unref();
|
|
7148
7148
|
console.log(
|
|
7149
|
-
`${import_picocolors.default.magenta("\
|
|
7149
|
+
`${import_picocolors.default.magenta("\u273F")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(Claude Code)")}`
|
|
7150
7150
|
);
|
|
7151
7151
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${DEFAULT_PORT}`)}`);
|
package/dist/cli.js
CHANGED
|
@@ -7130,7 +7130,7 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
7130
7130
|
var DEFAULT_PORT = 4567;
|
|
7131
7131
|
|
|
7132
7132
|
// src/cli.ts
|
|
7133
|
-
var VERSION = "0.0.
|
|
7133
|
+
var VERSION = "0.0.86";
|
|
7134
7134
|
var serverPath = join(__dirname, "server.cjs");
|
|
7135
7135
|
execa(process.execPath, [serverPath], {
|
|
7136
7136
|
detached: true,
|
|
@@ -7138,6 +7138,6 @@ execa(process.execPath, [serverPath], {
|
|
|
7138
7138
|
cleanup: false
|
|
7139
7139
|
}).unref();
|
|
7140
7140
|
console.log(
|
|
7141
|
-
`${import_picocolors.default.magenta("\
|
|
7141
|
+
`${import_picocolors.default.magenta("\u273F")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(Claude Code)")}`
|
|
7142
7142
|
);
|
|
7143
7143
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${DEFAULT_PORT}`)}`);
|
package/dist/client.cjs
CHANGED
|
@@ -55,6 +55,7 @@ async function* streamSSE(stream, signal) {
|
|
|
55
55
|
|
|
56
56
|
// src/constants.ts
|
|
57
57
|
var DEFAULT_PORT = 4567;
|
|
58
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
58
59
|
|
|
59
60
|
// src/client.ts
|
|
60
61
|
var DEFAULT_SERVER_URL = `http://localhost:${DEFAULT_PORT}`;
|
|
@@ -98,6 +99,7 @@ async function* streamFromServer(serverUrl, context, signal) {
|
|
|
98
99
|
const iterator = streamSSE(response.body, signal)[Symbol.asyncIterator]();
|
|
99
100
|
let done = false;
|
|
100
101
|
let pendingNext = iterator.next();
|
|
102
|
+
let lastStatus = null;
|
|
101
103
|
while (!done) {
|
|
102
104
|
const result = await Promise.race([
|
|
103
105
|
pendingNext.then((iteratorResult) => ({
|
|
@@ -108,23 +110,22 @@ async function* streamFromServer(serverUrl, context, signal) {
|
|
|
108
110
|
(resolve) => setTimeout(() => resolve({ type: "timeout" }), 100)
|
|
109
111
|
)
|
|
110
112
|
]);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
yield `Working\u2026 ${elapsedSeconds.toFixed(1)}s`;
|
|
114
|
-
} else {
|
|
113
|
+
const elapsedSeconds = (Date.now() - startTime) / 1e3;
|
|
114
|
+
if (result.type === "status") {
|
|
115
115
|
const iteratorResult = result.iteratorResult;
|
|
116
116
|
done = iteratorResult.done ?? false;
|
|
117
117
|
if (!done && iteratorResult.value) {
|
|
118
|
-
|
|
119
|
-
if (status === "Completed successfully") {
|
|
120
|
-
const totalSeconds = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
121
|
-
yield `Completed in ${totalSeconds}s`;
|
|
122
|
-
} else {
|
|
123
|
-
yield status;
|
|
124
|
-
}
|
|
118
|
+
lastStatus = iteratorResult.value;
|
|
125
119
|
pendingNext = iterator.next();
|
|
126
120
|
}
|
|
127
121
|
}
|
|
122
|
+
if (lastStatus === COMPLETED_STATUS) {
|
|
123
|
+
yield `Completed in ${elapsedSeconds.toFixed(1)}s`;
|
|
124
|
+
} else if (lastStatus) {
|
|
125
|
+
yield `${lastStatus} ${elapsedSeconds.toFixed(1)}s`;
|
|
126
|
+
} else {
|
|
127
|
+
yield `Working\u2026 ${elapsedSeconds.toFixed(1)}s`;
|
|
128
|
+
}
|
|
128
129
|
}
|
|
129
130
|
} finally {
|
|
130
131
|
signal.removeEventListener("abort", handleAbort);
|
|
@@ -180,6 +181,12 @@ var createClaudeAgentProvider = (providerOptions = {}) => {
|
|
|
180
181
|
connectionCache = { result: false, timestamp: now };
|
|
181
182
|
return false;
|
|
182
183
|
}
|
|
184
|
+
},
|
|
185
|
+
undo: async () => {
|
|
186
|
+
try {
|
|
187
|
+
await fetch(`${serverUrl}/undo`, { method: "POST" });
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
183
190
|
}
|
|
184
191
|
};
|
|
185
192
|
};
|
package/dist/client.d.cts
CHANGED
package/dist/client.d.ts
CHANGED
package/dist/client.global.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var ReactGrabClaudeCode=(function(exports){'use strict';var
|
|
2
|
-
`))
|
|
1
|
+
var ReactGrabClaudeCode=(function(exports){'use strict';var f=5e3,y="react-grab:agent-sessions",E=c=>{let e="",n="";for(let o of c.split(`
|
|
2
|
+
`))o.startsWith("event:")?e=o.slice(6).trim():o.startsWith("data:")&&(n=o.slice(5).trim());return {eventType:e,data:n}};async function*g(c,e){let n=c.getReader(),o=new TextDecoder,s="",t=false,r=()=>{t=true,n.cancel().catch(()=>{});};e.addEventListener("abort",r);try{if(e.aborted)throw new DOMException("Aborted","AbortError");for(;;){let a=await n.read();if(t||e.aborted)throw new DOMException("Aborted","AbortError");let{done:l,value:m}=a;m&&(s+=o.decode(m,{stream:!0}));let i;for(;(i=s.indexOf(`
|
|
3
3
|
|
|
4
|
-
`))!==-1;){let{eventType:
|
|
4
|
+
`))!==-1;){let{eventType:u,data:d}=E(s.slice(0,i));if(s=s.slice(i+2),u==="done")return;if(u==="error")throw new Error(d||"Agent error");d&&(yield d);}if(l)break}}finally{e.removeEventListener("abort",r);try{n.releaseLock();}catch{}}}var A="Completed successfully";var w=`http://localhost:${4567}`,b={systemPrompt:{type:"preset",preset:"claude_code",append:`You are helping a user make changes to a React component based on a selected element.
|
|
5
5
|
The user has selected an element from their UI and wants you to help modify it.
|
|
6
|
-
Provide clear, concise status updates as you work.`},model:"haiku",permissionMode:"bypassPermissions",maxTurns:10};async function*
|
|
6
|
+
Provide clear, concise status updates as you work.`},model:"haiku",permissionMode:"bypassPermissions",maxTurns:10};async function*h(c,e,n){let o=Date.now(),s=e.sessionId,t=()=>{s&&fetch(`${c}/abort/${s}`,{method:"POST"}).catch(()=>{});};n.addEventListener("abort",t);try{let r=await fetch(`${c}/agent`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:n});if(!r.ok)throw new Error(`Server error: ${r.status}`);if(!r.body)throw new Error("No response body");let a=g(r.body,n)[Symbol.asyncIterator](),l=!1,m=a.next(),i=null;for(;!l;){let u=await Promise.race([m.then(p=>({type:"status",iteratorResult:p})),new Promise(p=>setTimeout(()=>p({type:"timeout"}),100))]),d=(Date.now()-o)/1e3;if(u.type==="status"){let p=u.iteratorResult;l=p.done??!1,!l&&p.value&&(i=p.value,m=a.next());}i===A?yield `Completed in ${d.toFixed(1)}s`:i?yield `${i} ${d.toFixed(1)}s`:yield `Working\u2026 ${d.toFixed(1)}s`;}}finally{n.removeEventListener("abort",t);}}var S=(c={})=>{let{serverUrl:e=w,getOptions:n}=c,o=null,s=t=>({...b,...n?.()??{},...t??{}});return {send:async function*(t,r){let a={...t,options:s(t.options)};yield*h(e,a,r);},resume:async function*(t,r,a){let l=a.getItem(y);if(!l)throw new Error("No sessions to resume");let i=JSON.parse(l)[t];if(!i)throw new Error(`Session ${t} not found`);let u=i.context,d={...u,options:s(u.options)};yield "Resuming...",yield*h(e,d,r);},supportsResume:true,supportsFollowUp:true,checkConnection:async()=>{let t=Date.now();if(o&&t-o.timestamp<f)return o.result;try{let a=(await fetch(`${e}/health`,{method:"GET"})).ok;return o={result:a,timestamp:t},a}catch{return o={result:false,timestamp:t},false}},undo:async()=>{try{await fetch(`${e}/undo`,{method:"POST"});}catch{}}}},T=async()=>{if(typeof window>"u")return;let c=S(),e=s=>{s.setAgent({provider:c,storage:sessionStorage});},n=window.__REACT_GRAB__;if(n){e(n);return}window.addEventListener("react-grab:init",s=>{e(s.detail);},{once:true});let o=window.__REACT_GRAB__;o&&e(o);};T();exports.attachAgent=T;exports.createClaudeAgentProvider=S;return exports;})({});
|
package/dist/client.js
CHANGED
|
@@ -53,6 +53,7 @@ async function* streamSSE(stream, signal) {
|
|
|
53
53
|
|
|
54
54
|
// src/constants.ts
|
|
55
55
|
var DEFAULT_PORT = 4567;
|
|
56
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
56
57
|
|
|
57
58
|
// src/client.ts
|
|
58
59
|
var DEFAULT_SERVER_URL = `http://localhost:${DEFAULT_PORT}`;
|
|
@@ -96,6 +97,7 @@ async function* streamFromServer(serverUrl, context, signal) {
|
|
|
96
97
|
const iterator = streamSSE(response.body, signal)[Symbol.asyncIterator]();
|
|
97
98
|
let done = false;
|
|
98
99
|
let pendingNext = iterator.next();
|
|
100
|
+
let lastStatus = null;
|
|
99
101
|
while (!done) {
|
|
100
102
|
const result = await Promise.race([
|
|
101
103
|
pendingNext.then((iteratorResult) => ({
|
|
@@ -106,23 +108,22 @@ async function* streamFromServer(serverUrl, context, signal) {
|
|
|
106
108
|
(resolve) => setTimeout(() => resolve({ type: "timeout" }), 100)
|
|
107
109
|
)
|
|
108
110
|
]);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
yield `Working\u2026 ${elapsedSeconds.toFixed(1)}s`;
|
|
112
|
-
} else {
|
|
111
|
+
const elapsedSeconds = (Date.now() - startTime) / 1e3;
|
|
112
|
+
if (result.type === "status") {
|
|
113
113
|
const iteratorResult = result.iteratorResult;
|
|
114
114
|
done = iteratorResult.done ?? false;
|
|
115
115
|
if (!done && iteratorResult.value) {
|
|
116
|
-
|
|
117
|
-
if (status === "Completed successfully") {
|
|
118
|
-
const totalSeconds = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
119
|
-
yield `Completed in ${totalSeconds}s`;
|
|
120
|
-
} else {
|
|
121
|
-
yield status;
|
|
122
|
-
}
|
|
116
|
+
lastStatus = iteratorResult.value;
|
|
123
117
|
pendingNext = iterator.next();
|
|
124
118
|
}
|
|
125
119
|
}
|
|
120
|
+
if (lastStatus === COMPLETED_STATUS) {
|
|
121
|
+
yield `Completed in ${elapsedSeconds.toFixed(1)}s`;
|
|
122
|
+
} else if (lastStatus) {
|
|
123
|
+
yield `${lastStatus} ${elapsedSeconds.toFixed(1)}s`;
|
|
124
|
+
} else {
|
|
125
|
+
yield `Working\u2026 ${elapsedSeconds.toFixed(1)}s`;
|
|
126
|
+
}
|
|
126
127
|
}
|
|
127
128
|
} finally {
|
|
128
129
|
signal.removeEventListener("abort", handleAbort);
|
|
@@ -178,6 +179,12 @@ var createClaudeAgentProvider = (providerOptions = {}) => {
|
|
|
178
179
|
connectionCache = { result: false, timestamp: now };
|
|
179
180
|
return false;
|
|
180
181
|
}
|
|
182
|
+
},
|
|
183
|
+
undo: async () => {
|
|
184
|
+
try {
|
|
185
|
+
await fetch(`${serverUrl}/undo`, { method: "POST" });
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
181
188
|
}
|
|
182
189
|
};
|
|
183
190
|
};
|
package/dist/server.cjs
CHANGED
|
@@ -18156,12 +18156,43 @@ function query({
|
|
|
18156
18156
|
|
|
18157
18157
|
// src/constants.ts
|
|
18158
18158
|
var DEFAULT_PORT = 4567;
|
|
18159
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
18159
18160
|
|
|
18160
18161
|
// ../utils/dist/server.js
|
|
18161
18162
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
18163
|
+
var COMMAND_INSTALL_MAP = {
|
|
18164
|
+
"cursor-agent": "Install Cursor (https://cursor.com) and ensure 'cursor-agent' is in your PATH.",
|
|
18165
|
+
gemini: "Install the Gemini CLI: npm install -g @anthropic-ai/gemini-cli\nOr see: https://github.com/google-gemini/gemini-cli",
|
|
18166
|
+
claude: "Install Claude Code: npm install -g @anthropic-ai/claude-code\nOr see: https://github.com/anthropics/claude-code"
|
|
18167
|
+
};
|
|
18168
|
+
var formatSpawnError = (error, commandName) => {
|
|
18169
|
+
const spawnError = error;
|
|
18170
|
+
const isNotFound = spawnError.code === "ENOENT" || spawnError.message && spawnError.message.includes("ENOENT");
|
|
18171
|
+
if (isNotFound) {
|
|
18172
|
+
const installInfo = COMMAND_INSTALL_MAP[commandName];
|
|
18173
|
+
const baseMessage = `Command '${commandName}' not found.`;
|
|
18174
|
+
{
|
|
18175
|
+
return `${baseMessage}
|
|
18176
|
+
|
|
18177
|
+
${installInfo}`;
|
|
18178
|
+
}
|
|
18179
|
+
}
|
|
18180
|
+
const isPermissionDenied = spawnError.code === "EACCES" || spawnError.message && spawnError.message.includes("EACCES");
|
|
18181
|
+
if (isPermissionDenied) {
|
|
18182
|
+
return `Permission denied when trying to run '${commandName}'.
|
|
18183
|
+
|
|
18184
|
+
Check that the command is executable: chmod +x $(which ${commandName})`;
|
|
18185
|
+
}
|
|
18186
|
+
return error.message;
|
|
18187
|
+
};
|
|
18162
18188
|
|
|
18163
18189
|
// src/server.ts
|
|
18164
|
-
var VERSION = "0.0.
|
|
18190
|
+
var VERSION = "0.0.86";
|
|
18191
|
+
try {
|
|
18192
|
+
fetch(`https://www.react-grab.com/api/version?source=claude-code&t=${Date.now()}`).catch(() => {
|
|
18193
|
+
});
|
|
18194
|
+
} catch {
|
|
18195
|
+
}
|
|
18165
18196
|
var resolveClaudePath = () => {
|
|
18166
18197
|
const command = process.platform === "win32" ? "where claude" : "which claude";
|
|
18167
18198
|
try {
|
|
@@ -18173,6 +18204,7 @@ var resolveClaudePath = () => {
|
|
|
18173
18204
|
};
|
|
18174
18205
|
var claudeSessionMap = /* @__PURE__ */ new Map();
|
|
18175
18206
|
var abortedSessions = /* @__PURE__ */ new Set();
|
|
18207
|
+
var lastClaudeSessionId;
|
|
18176
18208
|
var isTextBlock = (block) => block.type === "text";
|
|
18177
18209
|
var createServer = () => {
|
|
18178
18210
|
const app = new Hono2();
|
|
@@ -18188,7 +18220,7 @@ ${content}`;
|
|
|
18188
18220
|
return streamSSE(context, async (stream2) => {
|
|
18189
18221
|
const isAborted2 = () => sessionId && abortedSessions.has(sessionId);
|
|
18190
18222
|
try {
|
|
18191
|
-
await stream2.writeSSE({ data: "Thinking
|
|
18223
|
+
await stream2.writeSSE({ data: "Thinking\u2026", event: "status" });
|
|
18192
18224
|
const env = { ...process.env };
|
|
18193
18225
|
delete env.NODE_OPTIONS;
|
|
18194
18226
|
delete env.VSCODE_INSPECTOR_OPTIONS;
|
|
@@ -18196,10 +18228,10 @@ ${content}`;
|
|
|
18196
18228
|
prompt: userPrompt,
|
|
18197
18229
|
options: {
|
|
18198
18230
|
pathToClaudeCodeExecutable: resolveClaudePath(),
|
|
18199
|
-
cwd: process.cwd(),
|
|
18200
18231
|
includePartialMessages: true,
|
|
18201
18232
|
env,
|
|
18202
18233
|
...options,
|
|
18234
|
+
cwd: options?.cwd ?? process.env.REACT_GRAB_CWD ?? process.cwd(),
|
|
18203
18235
|
...isFollowUp && claudeSessionId ? { resume: claudeSessionId } : {}
|
|
18204
18236
|
}
|
|
18205
18237
|
});
|
|
@@ -18217,7 +18249,7 @@ ${content}`;
|
|
|
18217
18249
|
}
|
|
18218
18250
|
if (message.type === "result") {
|
|
18219
18251
|
await stream2.writeSSE({
|
|
18220
|
-
data: message.subtype === "success" ?
|
|
18252
|
+
data: message.subtype === "success" ? COMPLETED_STATUS : "Task finished",
|
|
18221
18253
|
event: "status"
|
|
18222
18254
|
});
|
|
18223
18255
|
}
|
|
@@ -18226,13 +18258,14 @@ ${content}`;
|
|
|
18226
18258
|
if (sessionId) {
|
|
18227
18259
|
claudeSessionMap.set(sessionId, capturedClaudeSessionId);
|
|
18228
18260
|
}
|
|
18261
|
+
lastClaudeSessionId = capturedClaudeSessionId;
|
|
18229
18262
|
}
|
|
18230
18263
|
if (!isAborted2()) {
|
|
18231
18264
|
await stream2.writeSSE({ data: "", event: "done" });
|
|
18232
18265
|
}
|
|
18233
18266
|
} catch (error) {
|
|
18234
18267
|
if (!isAborted2()) {
|
|
18235
|
-
const errorMessage = error instanceof Error ? error
|
|
18268
|
+
const errorMessage = error instanceof Error ? formatSpawnError(error, "claude") : "Unknown error";
|
|
18236
18269
|
const stderr = error instanceof Error && "stderr" in error ? String(error.stderr) : void 0;
|
|
18237
18270
|
const fullError = stderr && stderr.trim() ? `${errorMessage}
|
|
18238
18271
|
|
|
@@ -18255,6 +18288,31 @@ ${stderr.trim()}` : errorMessage;
|
|
|
18255
18288
|
abortedSessions.add(sessionId);
|
|
18256
18289
|
return context.json({ status: "ok" });
|
|
18257
18290
|
});
|
|
18291
|
+
app.post("/undo", async (context) => {
|
|
18292
|
+
if (!lastClaudeSessionId) {
|
|
18293
|
+
return context.json({ status: "error", message: "No session to undo" });
|
|
18294
|
+
}
|
|
18295
|
+
try {
|
|
18296
|
+
const env = { ...process.env };
|
|
18297
|
+
delete env.NODE_OPTIONS;
|
|
18298
|
+
delete env.VSCODE_INSPECTOR_OPTIONS;
|
|
18299
|
+
const queryResult = query({
|
|
18300
|
+
prompt: "undo",
|
|
18301
|
+
options: {
|
|
18302
|
+
pathToClaudeCodeExecutable: resolveClaudePath(),
|
|
18303
|
+
env,
|
|
18304
|
+
cwd: process.env.REACT_GRAB_CWD ?? process.cwd(),
|
|
18305
|
+
resume: lastClaudeSessionId
|
|
18306
|
+
}
|
|
18307
|
+
});
|
|
18308
|
+
for await (const _message of queryResult) {
|
|
18309
|
+
}
|
|
18310
|
+
return context.json({ status: "ok" });
|
|
18311
|
+
} catch (error) {
|
|
18312
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
18313
|
+
return context.json({ status: "error", message: errorMessage });
|
|
18314
|
+
}
|
|
18315
|
+
});
|
|
18258
18316
|
app.get("/health", (context) => {
|
|
18259
18317
|
return context.json({ status: "ok", provider: "claude" });
|
|
18260
18318
|
});
|
|
@@ -18267,7 +18325,7 @@ var startServer = async (port = DEFAULT_PORT) => {
|
|
|
18267
18325
|
const app = createServer();
|
|
18268
18326
|
serve({ fetch: app.fetch, port });
|
|
18269
18327
|
console.log(
|
|
18270
|
-
`${import_picocolors.default.magenta("\
|
|
18328
|
+
`${import_picocolors.default.magenta("\u273F")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(Claude Code)")}`
|
|
18271
18329
|
);
|
|
18272
18330
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${port}`)}`);
|
|
18273
18331
|
};
|
package/dist/server.js
CHANGED
|
@@ -18127,12 +18127,43 @@ function query({
|
|
|
18127
18127
|
|
|
18128
18128
|
// src/constants.ts
|
|
18129
18129
|
var DEFAULT_PORT = 4567;
|
|
18130
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
18130
18131
|
|
|
18131
18132
|
// ../utils/dist/server.js
|
|
18132
18133
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
18134
|
+
var COMMAND_INSTALL_MAP = {
|
|
18135
|
+
"cursor-agent": "Install Cursor (https://cursor.com) and ensure 'cursor-agent' is in your PATH.",
|
|
18136
|
+
gemini: "Install the Gemini CLI: npm install -g @anthropic-ai/gemini-cli\nOr see: https://github.com/google-gemini/gemini-cli",
|
|
18137
|
+
claude: "Install Claude Code: npm install -g @anthropic-ai/claude-code\nOr see: https://github.com/anthropics/claude-code"
|
|
18138
|
+
};
|
|
18139
|
+
var formatSpawnError = (error, commandName) => {
|
|
18140
|
+
const spawnError = error;
|
|
18141
|
+
const isNotFound = spawnError.code === "ENOENT" || spawnError.message && spawnError.message.includes("ENOENT");
|
|
18142
|
+
if (isNotFound) {
|
|
18143
|
+
const installInfo = COMMAND_INSTALL_MAP[commandName];
|
|
18144
|
+
const baseMessage = `Command '${commandName}' not found.`;
|
|
18145
|
+
{
|
|
18146
|
+
return `${baseMessage}
|
|
18147
|
+
|
|
18148
|
+
${installInfo}`;
|
|
18149
|
+
}
|
|
18150
|
+
}
|
|
18151
|
+
const isPermissionDenied = spawnError.code === "EACCES" || spawnError.message && spawnError.message.includes("EACCES");
|
|
18152
|
+
if (isPermissionDenied) {
|
|
18153
|
+
return `Permission denied when trying to run '${commandName}'.
|
|
18154
|
+
|
|
18155
|
+
Check that the command is executable: chmod +x $(which ${commandName})`;
|
|
18156
|
+
}
|
|
18157
|
+
return error.message;
|
|
18158
|
+
};
|
|
18133
18159
|
|
|
18134
18160
|
// src/server.ts
|
|
18135
|
-
var VERSION = "0.0.
|
|
18161
|
+
var VERSION = "0.0.86";
|
|
18162
|
+
try {
|
|
18163
|
+
fetch(`https://www.react-grab.com/api/version?source=claude-code&t=${Date.now()}`).catch(() => {
|
|
18164
|
+
});
|
|
18165
|
+
} catch {
|
|
18166
|
+
}
|
|
18136
18167
|
var resolveClaudePath = () => {
|
|
18137
18168
|
const command = process.platform === "win32" ? "where claude" : "which claude";
|
|
18138
18169
|
try {
|
|
@@ -18144,6 +18175,7 @@ var resolveClaudePath = () => {
|
|
|
18144
18175
|
};
|
|
18145
18176
|
var claudeSessionMap = /* @__PURE__ */ new Map();
|
|
18146
18177
|
var abortedSessions = /* @__PURE__ */ new Set();
|
|
18178
|
+
var lastClaudeSessionId;
|
|
18147
18179
|
var isTextBlock = (block) => block.type === "text";
|
|
18148
18180
|
var createServer = () => {
|
|
18149
18181
|
const app = new Hono2();
|
|
@@ -18159,7 +18191,7 @@ ${content}`;
|
|
|
18159
18191
|
return streamSSE(context, async (stream2) => {
|
|
18160
18192
|
const isAborted2 = () => sessionId && abortedSessions.has(sessionId);
|
|
18161
18193
|
try {
|
|
18162
|
-
await stream2.writeSSE({ data: "Thinking
|
|
18194
|
+
await stream2.writeSSE({ data: "Thinking\u2026", event: "status" });
|
|
18163
18195
|
const env = { ...process.env };
|
|
18164
18196
|
delete env.NODE_OPTIONS;
|
|
18165
18197
|
delete env.VSCODE_INSPECTOR_OPTIONS;
|
|
@@ -18167,10 +18199,10 @@ ${content}`;
|
|
|
18167
18199
|
prompt: userPrompt,
|
|
18168
18200
|
options: {
|
|
18169
18201
|
pathToClaudeCodeExecutable: resolveClaudePath(),
|
|
18170
|
-
cwd: process.cwd(),
|
|
18171
18202
|
includePartialMessages: true,
|
|
18172
18203
|
env,
|
|
18173
18204
|
...options,
|
|
18205
|
+
cwd: options?.cwd ?? process.env.REACT_GRAB_CWD ?? process.cwd(),
|
|
18174
18206
|
...isFollowUp && claudeSessionId ? { resume: claudeSessionId } : {}
|
|
18175
18207
|
}
|
|
18176
18208
|
});
|
|
@@ -18188,7 +18220,7 @@ ${content}`;
|
|
|
18188
18220
|
}
|
|
18189
18221
|
if (message.type === "result") {
|
|
18190
18222
|
await stream2.writeSSE({
|
|
18191
|
-
data: message.subtype === "success" ?
|
|
18223
|
+
data: message.subtype === "success" ? COMPLETED_STATUS : "Task finished",
|
|
18192
18224
|
event: "status"
|
|
18193
18225
|
});
|
|
18194
18226
|
}
|
|
@@ -18197,13 +18229,14 @@ ${content}`;
|
|
|
18197
18229
|
if (sessionId) {
|
|
18198
18230
|
claudeSessionMap.set(sessionId, capturedClaudeSessionId);
|
|
18199
18231
|
}
|
|
18232
|
+
lastClaudeSessionId = capturedClaudeSessionId;
|
|
18200
18233
|
}
|
|
18201
18234
|
if (!isAborted2()) {
|
|
18202
18235
|
await stream2.writeSSE({ data: "", event: "done" });
|
|
18203
18236
|
}
|
|
18204
18237
|
} catch (error) {
|
|
18205
18238
|
if (!isAborted2()) {
|
|
18206
|
-
const errorMessage = error instanceof Error ? error
|
|
18239
|
+
const errorMessage = error instanceof Error ? formatSpawnError(error, "claude") : "Unknown error";
|
|
18207
18240
|
const stderr = error instanceof Error && "stderr" in error ? String(error.stderr) : void 0;
|
|
18208
18241
|
const fullError = stderr && stderr.trim() ? `${errorMessage}
|
|
18209
18242
|
|
|
@@ -18226,6 +18259,31 @@ ${stderr.trim()}` : errorMessage;
|
|
|
18226
18259
|
abortedSessions.add(sessionId);
|
|
18227
18260
|
return context.json({ status: "ok" });
|
|
18228
18261
|
});
|
|
18262
|
+
app.post("/undo", async (context) => {
|
|
18263
|
+
if (!lastClaudeSessionId) {
|
|
18264
|
+
return context.json({ status: "error", message: "No session to undo" });
|
|
18265
|
+
}
|
|
18266
|
+
try {
|
|
18267
|
+
const env = { ...process.env };
|
|
18268
|
+
delete env.NODE_OPTIONS;
|
|
18269
|
+
delete env.VSCODE_INSPECTOR_OPTIONS;
|
|
18270
|
+
const queryResult = query({
|
|
18271
|
+
prompt: "undo",
|
|
18272
|
+
options: {
|
|
18273
|
+
pathToClaudeCodeExecutable: resolveClaudePath(),
|
|
18274
|
+
env,
|
|
18275
|
+
cwd: process.env.REACT_GRAB_CWD ?? process.cwd(),
|
|
18276
|
+
resume: lastClaudeSessionId
|
|
18277
|
+
}
|
|
18278
|
+
});
|
|
18279
|
+
for await (const _message of queryResult) {
|
|
18280
|
+
}
|
|
18281
|
+
return context.json({ status: "ok" });
|
|
18282
|
+
} catch (error) {
|
|
18283
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
18284
|
+
return context.json({ status: "error", message: errorMessage });
|
|
18285
|
+
}
|
|
18286
|
+
});
|
|
18229
18287
|
app.get("/health", (context) => {
|
|
18230
18288
|
return context.json({ status: "ok", provider: "claude" });
|
|
18231
18289
|
});
|
|
@@ -18238,7 +18296,7 @@ var startServer = async (port = DEFAULT_PORT) => {
|
|
|
18238
18296
|
const app = createServer();
|
|
18239
18297
|
serve({ fetch: app.fetch, port });
|
|
18240
18298
|
console.log(
|
|
18241
|
-
`${import_picocolors.default.magenta("\
|
|
18299
|
+
`${import_picocolors.default.magenta("\u273F")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(Claude Code)")}`
|
|
18242
18300
|
);
|
|
18243
18301
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${port}`)}`);
|
|
18244
18302
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-grab/claude-code",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.86",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"react-grab-claude-code": "./dist/cli.cjs"
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
"dist"
|
|
25
25
|
],
|
|
26
26
|
"devDependencies": {
|
|
27
|
+
"@types/node": "^22.10.7",
|
|
27
28
|
"tsup": "^8.4.0",
|
|
28
|
-
"@react-grab/utils": "0.0.
|
|
29
|
+
"@react-grab/utils": "0.0.86"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
32
|
"@anthropic-ai/claude-agent-sdk": "^0.1.0",
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
"fkill": "^9.0.0",
|
|
35
36
|
"hono": "^4.0.0",
|
|
36
37
|
"picocolors": "^1.1.1",
|
|
37
|
-
"react-grab": "0.0.
|
|
38
|
+
"react-grab": "0.0.86"
|
|
38
39
|
},
|
|
39
40
|
"scripts": {
|
|
40
41
|
"dev": "tsup --watch",
|