@react-grab/claude-code 0.0.85 → 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 +59 -6
- package/dist/server.js +59 -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,38 @@ 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";
|
|
18165
18191
|
try {
|
|
18166
18192
|
fetch(`https://www.react-grab.com/api/version?source=claude-code&t=${Date.now()}`).catch(() => {
|
|
18167
18193
|
});
|
|
@@ -18178,6 +18204,7 @@ var resolveClaudePath = () => {
|
|
|
18178
18204
|
};
|
|
18179
18205
|
var claudeSessionMap = /* @__PURE__ */ new Map();
|
|
18180
18206
|
var abortedSessions = /* @__PURE__ */ new Set();
|
|
18207
|
+
var lastClaudeSessionId;
|
|
18181
18208
|
var isTextBlock = (block) => block.type === "text";
|
|
18182
18209
|
var createServer = () => {
|
|
18183
18210
|
const app = new Hono2();
|
|
@@ -18193,7 +18220,7 @@ ${content}`;
|
|
|
18193
18220
|
return streamSSE(context, async (stream2) => {
|
|
18194
18221
|
const isAborted2 = () => sessionId && abortedSessions.has(sessionId);
|
|
18195
18222
|
try {
|
|
18196
|
-
await stream2.writeSSE({ data: "Thinking
|
|
18223
|
+
await stream2.writeSSE({ data: "Thinking\u2026", event: "status" });
|
|
18197
18224
|
const env = { ...process.env };
|
|
18198
18225
|
delete env.NODE_OPTIONS;
|
|
18199
18226
|
delete env.VSCODE_INSPECTOR_OPTIONS;
|
|
@@ -18201,10 +18228,10 @@ ${content}`;
|
|
|
18201
18228
|
prompt: userPrompt,
|
|
18202
18229
|
options: {
|
|
18203
18230
|
pathToClaudeCodeExecutable: resolveClaudePath(),
|
|
18204
|
-
cwd: process.cwd(),
|
|
18205
18231
|
includePartialMessages: true,
|
|
18206
18232
|
env,
|
|
18207
18233
|
...options,
|
|
18234
|
+
cwd: options?.cwd ?? process.env.REACT_GRAB_CWD ?? process.cwd(),
|
|
18208
18235
|
...isFollowUp && claudeSessionId ? { resume: claudeSessionId } : {}
|
|
18209
18236
|
}
|
|
18210
18237
|
});
|
|
@@ -18222,7 +18249,7 @@ ${content}`;
|
|
|
18222
18249
|
}
|
|
18223
18250
|
if (message.type === "result") {
|
|
18224
18251
|
await stream2.writeSSE({
|
|
18225
|
-
data: message.subtype === "success" ?
|
|
18252
|
+
data: message.subtype === "success" ? COMPLETED_STATUS : "Task finished",
|
|
18226
18253
|
event: "status"
|
|
18227
18254
|
});
|
|
18228
18255
|
}
|
|
@@ -18231,13 +18258,14 @@ ${content}`;
|
|
|
18231
18258
|
if (sessionId) {
|
|
18232
18259
|
claudeSessionMap.set(sessionId, capturedClaudeSessionId);
|
|
18233
18260
|
}
|
|
18261
|
+
lastClaudeSessionId = capturedClaudeSessionId;
|
|
18234
18262
|
}
|
|
18235
18263
|
if (!isAborted2()) {
|
|
18236
18264
|
await stream2.writeSSE({ data: "", event: "done" });
|
|
18237
18265
|
}
|
|
18238
18266
|
} catch (error) {
|
|
18239
18267
|
if (!isAborted2()) {
|
|
18240
|
-
const errorMessage = error instanceof Error ? error
|
|
18268
|
+
const errorMessage = error instanceof Error ? formatSpawnError(error, "claude") : "Unknown error";
|
|
18241
18269
|
const stderr = error instanceof Error && "stderr" in error ? String(error.stderr) : void 0;
|
|
18242
18270
|
const fullError = stderr && stderr.trim() ? `${errorMessage}
|
|
18243
18271
|
|
|
@@ -18260,6 +18288,31 @@ ${stderr.trim()}` : errorMessage;
|
|
|
18260
18288
|
abortedSessions.add(sessionId);
|
|
18261
18289
|
return context.json({ status: "ok" });
|
|
18262
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
|
+
});
|
|
18263
18316
|
app.get("/health", (context) => {
|
|
18264
18317
|
return context.json({ status: "ok", provider: "claude" });
|
|
18265
18318
|
});
|
|
@@ -18272,7 +18325,7 @@ var startServer = async (port = DEFAULT_PORT) => {
|
|
|
18272
18325
|
const app = createServer();
|
|
18273
18326
|
serve({ fetch: app.fetch, port });
|
|
18274
18327
|
console.log(
|
|
18275
|
-
`${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)")}`
|
|
18276
18329
|
);
|
|
18277
18330
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${port}`)}`);
|
|
18278
18331
|
};
|
package/dist/server.js
CHANGED
|
@@ -18127,12 +18127,38 @@ 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";
|
|
18136
18162
|
try {
|
|
18137
18163
|
fetch(`https://www.react-grab.com/api/version?source=claude-code&t=${Date.now()}`).catch(() => {
|
|
18138
18164
|
});
|
|
@@ -18149,6 +18175,7 @@ var resolveClaudePath = () => {
|
|
|
18149
18175
|
};
|
|
18150
18176
|
var claudeSessionMap = /* @__PURE__ */ new Map();
|
|
18151
18177
|
var abortedSessions = /* @__PURE__ */ new Set();
|
|
18178
|
+
var lastClaudeSessionId;
|
|
18152
18179
|
var isTextBlock = (block) => block.type === "text";
|
|
18153
18180
|
var createServer = () => {
|
|
18154
18181
|
const app = new Hono2();
|
|
@@ -18164,7 +18191,7 @@ ${content}`;
|
|
|
18164
18191
|
return streamSSE(context, async (stream2) => {
|
|
18165
18192
|
const isAborted2 = () => sessionId && abortedSessions.has(sessionId);
|
|
18166
18193
|
try {
|
|
18167
|
-
await stream2.writeSSE({ data: "Thinking
|
|
18194
|
+
await stream2.writeSSE({ data: "Thinking\u2026", event: "status" });
|
|
18168
18195
|
const env = { ...process.env };
|
|
18169
18196
|
delete env.NODE_OPTIONS;
|
|
18170
18197
|
delete env.VSCODE_INSPECTOR_OPTIONS;
|
|
@@ -18172,10 +18199,10 @@ ${content}`;
|
|
|
18172
18199
|
prompt: userPrompt,
|
|
18173
18200
|
options: {
|
|
18174
18201
|
pathToClaudeCodeExecutable: resolveClaudePath(),
|
|
18175
|
-
cwd: process.cwd(),
|
|
18176
18202
|
includePartialMessages: true,
|
|
18177
18203
|
env,
|
|
18178
18204
|
...options,
|
|
18205
|
+
cwd: options?.cwd ?? process.env.REACT_GRAB_CWD ?? process.cwd(),
|
|
18179
18206
|
...isFollowUp && claudeSessionId ? { resume: claudeSessionId } : {}
|
|
18180
18207
|
}
|
|
18181
18208
|
});
|
|
@@ -18193,7 +18220,7 @@ ${content}`;
|
|
|
18193
18220
|
}
|
|
18194
18221
|
if (message.type === "result") {
|
|
18195
18222
|
await stream2.writeSSE({
|
|
18196
|
-
data: message.subtype === "success" ?
|
|
18223
|
+
data: message.subtype === "success" ? COMPLETED_STATUS : "Task finished",
|
|
18197
18224
|
event: "status"
|
|
18198
18225
|
});
|
|
18199
18226
|
}
|
|
@@ -18202,13 +18229,14 @@ ${content}`;
|
|
|
18202
18229
|
if (sessionId) {
|
|
18203
18230
|
claudeSessionMap.set(sessionId, capturedClaudeSessionId);
|
|
18204
18231
|
}
|
|
18232
|
+
lastClaudeSessionId = capturedClaudeSessionId;
|
|
18205
18233
|
}
|
|
18206
18234
|
if (!isAborted2()) {
|
|
18207
18235
|
await stream2.writeSSE({ data: "", event: "done" });
|
|
18208
18236
|
}
|
|
18209
18237
|
} catch (error) {
|
|
18210
18238
|
if (!isAborted2()) {
|
|
18211
|
-
const errorMessage = error instanceof Error ? error
|
|
18239
|
+
const errorMessage = error instanceof Error ? formatSpawnError(error, "claude") : "Unknown error";
|
|
18212
18240
|
const stderr = error instanceof Error && "stderr" in error ? String(error.stderr) : void 0;
|
|
18213
18241
|
const fullError = stderr && stderr.trim() ? `${errorMessage}
|
|
18214
18242
|
|
|
@@ -18231,6 +18259,31 @@ ${stderr.trim()}` : errorMessage;
|
|
|
18231
18259
|
abortedSessions.add(sessionId);
|
|
18232
18260
|
return context.json({ status: "ok" });
|
|
18233
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
|
+
});
|
|
18234
18287
|
app.get("/health", (context) => {
|
|
18235
18288
|
return context.json({ status: "ok", provider: "claude" });
|
|
18236
18289
|
});
|
|
@@ -18243,7 +18296,7 @@ var startServer = async (port = DEFAULT_PORT) => {
|
|
|
18243
18296
|
const app = createServer();
|
|
18244
18297
|
serve({ fetch: app.fetch, port });
|
|
18245
18298
|
console.log(
|
|
18246
|
-
`${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)")}`
|
|
18247
18300
|
);
|
|
18248
18301
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${port}`)}`);
|
|
18249
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",
|