@groupchatai/claude-runner 0.2.3 → 0.3.1
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/index.js +123 -55
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -240,11 +240,15 @@ var ALLOWED_TOOLS = [
|
|
|
240
240
|
"TodoWrite",
|
|
241
241
|
"NotebookEdit"
|
|
242
242
|
];
|
|
243
|
-
function spawnClaudeCode(prompt, config, runOptions) {
|
|
243
|
+
function spawnClaudeCode(prompt, config, runOptions, resumeSessionId) {
|
|
244
244
|
const format = config.verbose ? "stream-json" : "json";
|
|
245
|
-
const args = [
|
|
246
|
-
|
|
247
|
-
prompt
|
|
245
|
+
const args = [];
|
|
246
|
+
if (resumeSessionId) {
|
|
247
|
+
args.push("--resume", resumeSessionId, "-p", prompt);
|
|
248
|
+
} else {
|
|
249
|
+
args.push("-p", prompt);
|
|
250
|
+
}
|
|
251
|
+
args.push(
|
|
248
252
|
"--output-format",
|
|
249
253
|
format,
|
|
250
254
|
"--verbose",
|
|
@@ -252,7 +256,7 @@ function spawnClaudeCode(prompt, config, runOptions) {
|
|
|
252
256
|
"default",
|
|
253
257
|
"--allowedTools",
|
|
254
258
|
...ALLOWED_TOOLS
|
|
255
|
-
|
|
259
|
+
);
|
|
256
260
|
const model = runOptions?.model ?? config.model;
|
|
257
261
|
if (model) {
|
|
258
262
|
args.push("--model", model);
|
|
@@ -263,61 +267,97 @@ function spawnClaudeCode(prompt, config, runOptions) {
|
|
|
263
267
|
env: { ...process.env }
|
|
264
268
|
});
|
|
265
269
|
const pid = child.pid ?? 0;
|
|
266
|
-
const output = new Promise(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
270
|
+
const output = new Promise((resolve, reject) => {
|
|
271
|
+
const chunks = [];
|
|
272
|
+
const errChunks = [];
|
|
273
|
+
let lineBuf = "";
|
|
274
|
+
let lastResultJson = "";
|
|
275
|
+
let capturedSessionId;
|
|
276
|
+
let capturedPrUrl;
|
|
277
|
+
function checkEventForPrUrl(event) {
|
|
278
|
+
if (capturedPrUrl) return;
|
|
279
|
+
const texts = [];
|
|
280
|
+
if (event.message?.content) {
|
|
281
|
+
for (const block of event.message.content) {
|
|
282
|
+
if (block.text) texts.push(block.text);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (event.content_block?.text) texts.push(event.content_block.text);
|
|
286
|
+
if (typeof event.result === "string") texts.push(event.result);
|
|
287
|
+
const combined = texts.join("\n");
|
|
288
|
+
const match = combined.match(GITHUB_PR_URL_RE);
|
|
289
|
+
if (match) capturedPrUrl = match[match.length - 1];
|
|
290
|
+
}
|
|
291
|
+
child.stdout?.on("data", (data) => {
|
|
292
|
+
chunks.push(data);
|
|
293
|
+
const text = data.toString("utf-8");
|
|
294
|
+
lineBuf += text;
|
|
295
|
+
const lines = lineBuf.split("\n");
|
|
296
|
+
lineBuf = lines.pop() ?? "";
|
|
297
|
+
for (const line of lines) {
|
|
298
|
+
const trimmed = line.trim();
|
|
299
|
+
if (!trimmed) continue;
|
|
300
|
+
try {
|
|
301
|
+
const event = JSON.parse(trimmed);
|
|
302
|
+
if (event.type === "system" && event.subtype === "init" && event.session_id) {
|
|
303
|
+
capturedSessionId = event.session_id;
|
|
304
|
+
}
|
|
305
|
+
if (event.type === "result") lastResultJson = trimmed;
|
|
306
|
+
checkEventForPrUrl(event);
|
|
307
|
+
if (config.verbose) {
|
|
284
308
|
const formatted = formatStreamEvent(event, pid);
|
|
285
309
|
if (formatted) console.log(formatted);
|
|
286
|
-
}
|
|
310
|
+
}
|
|
311
|
+
} catch {
|
|
312
|
+
if (config.verbose) {
|
|
287
313
|
console.log(`${pidTag(pid)} ${C.dim}${trimmed}${C.reset}`);
|
|
288
314
|
}
|
|
289
315
|
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
child.stderr?.on("data", (data) => {
|
|
319
|
+
errChunks.push(data);
|
|
320
|
+
if (config.verbose) {
|
|
321
|
+
const text = data.toString("utf-8").trimEnd();
|
|
322
|
+
if (text) console.error(`${pidTag(pid)} ${C.dim}${text}${C.reset}`);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
child.on("error", (err) => reject(new Error(`Failed to spawn claude: ${err.message}`)));
|
|
326
|
+
child.on("close", (code) => {
|
|
327
|
+
if (lineBuf.trim()) {
|
|
328
|
+
try {
|
|
329
|
+
const event = JSON.parse(lineBuf.trim());
|
|
330
|
+
if (event.type === "system" && event.subtype === "init" && event.session_id) {
|
|
331
|
+
capturedSessionId = event.session_id;
|
|
332
|
+
}
|
|
333
|
+
if (event.type === "result") lastResultJson = lineBuf.trim();
|
|
334
|
+
checkEventForPrUrl(event);
|
|
335
|
+
if (config.verbose) {
|
|
304
336
|
const formatted = formatStreamEvent(event, pid);
|
|
305
337
|
if (formatted) console.log(formatted);
|
|
306
|
-
}
|
|
338
|
+
}
|
|
339
|
+
} catch {
|
|
340
|
+
if (config.verbose) {
|
|
307
341
|
console.log(`${pidTag(pid)} ${C.dim}${lineBuf.trim()}${C.reset}`);
|
|
308
342
|
}
|
|
309
343
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
344
|
+
}
|
|
345
|
+
const rawOutput = Buffer.concat(chunks).toString("utf-8");
|
|
346
|
+
const stdout = config.verbose ? lastResultJson || rawOutput : rawOutput;
|
|
347
|
+
const stderr = Buffer.concat(errChunks).toString("utf-8");
|
|
348
|
+
if (code !== 0 && stdout.trim().length === 0) {
|
|
349
|
+
reject(new Error(`claude exited with code ${code}: ${stderr}`));
|
|
350
|
+
} else {
|
|
351
|
+
resolve({
|
|
352
|
+
stdout,
|
|
353
|
+
rawOutput,
|
|
354
|
+
exitCode: code ?? 0,
|
|
355
|
+
sessionId: capturedSessionId,
|
|
356
|
+
streamPrUrl: capturedPrUrl
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
});
|
|
321
361
|
return { process: child, output };
|
|
322
362
|
}
|
|
323
363
|
function findResultEvent(stdout) {
|
|
@@ -404,6 +444,7 @@ function runShellCommand(cmd, args, cwd) {
|
|
|
404
444
|
});
|
|
405
445
|
});
|
|
406
446
|
}
|
|
447
|
+
var sessionCache = /* @__PURE__ */ new Map();
|
|
407
448
|
async function processRun(client, run, config) {
|
|
408
449
|
const runTag = ` ${C.pid}[${run.id.slice(-8)}]${C.reset}`;
|
|
409
450
|
const log = (msg) => console.log(`${runTag} ${msg}`);
|
|
@@ -446,14 +487,41 @@ async function processRun(client, run, config) {
|
|
|
446
487
|
});
|
|
447
488
|
await new Promise((resolve) => git.on("close", () => resolve()));
|
|
448
489
|
}
|
|
490
|
+
const cachedSessionId = sessionCache.get(run.taskId);
|
|
491
|
+
const isFollowUp = cachedSessionId !== void 0;
|
|
492
|
+
const effectivePrompt = isFollowUp ? detail.prompt : prompt;
|
|
493
|
+
const resumeSession = isFollowUp ? cachedSessionId : void 0;
|
|
449
494
|
if (config.verbose) {
|
|
450
|
-
|
|
495
|
+
if (isFollowUp) {
|
|
496
|
+
log(`\u{1F504} Resuming session ${cachedSessionId.slice(0, 12)}\u2026`);
|
|
497
|
+
}
|
|
498
|
+
log(`\u{1F4DD} Prompt (${effectivePrompt.length} chars)`);
|
|
451
499
|
if (effectiveModel) log(`\u{1F9E0} Model: ${effectiveModel}`);
|
|
452
500
|
}
|
|
453
|
-
const { process: child, output } = spawnClaudeCode(
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
501
|
+
const { process: child, output } = spawnClaudeCode(
|
|
502
|
+
effectivePrompt,
|
|
503
|
+
config,
|
|
504
|
+
runOptions,
|
|
505
|
+
resumeSession
|
|
506
|
+
);
|
|
507
|
+
log(`\u{1F916} Claude Code spawned (pid ${child.pid})${isFollowUp ? " (follow-up)" : ""}`);
|
|
508
|
+
let { stdout, rawOutput, exitCode, sessionId, streamPrUrl } = await output;
|
|
509
|
+
if (exitCode !== 0 && isFollowUp) {
|
|
510
|
+
log(`\u26A0 Session resume failed, retrying with fresh session\u2026`);
|
|
511
|
+
sessionCache.delete(run.taskId);
|
|
512
|
+
const retry = spawnClaudeCode(prompt, config, runOptions);
|
|
513
|
+
log(`\u{1F916} Claude Code spawned (pid ${retry.process.pid}) (fresh)`);
|
|
514
|
+
const retryResult = await retry.output;
|
|
515
|
+
stdout = retryResult.stdout;
|
|
516
|
+
rawOutput = retryResult.rawOutput;
|
|
517
|
+
exitCode = retryResult.exitCode;
|
|
518
|
+
sessionId = retryResult.sessionId;
|
|
519
|
+
streamPrUrl = retryResult.streamPrUrl;
|
|
520
|
+
}
|
|
521
|
+
if (sessionId) {
|
|
522
|
+
sessionCache.set(run.taskId, sessionId);
|
|
523
|
+
}
|
|
524
|
+
const pullRequestUrl = streamPrUrl ?? await detectPullRequestUrl(config.workDir) ?? extractPullRequestUrlFromOutput(stdout) ?? extractPullRequestUrlFromOutput(rawOutput);
|
|
457
525
|
if (exitCode !== 0) {
|
|
458
526
|
const errorMsg = `Claude Code exited with code ${exitCode}:
|
|
459
527
|
\`\`\`
|