@groupchatai/claude-runner 0.4.8 → 0.4.10
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 +218 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,7 +79,7 @@ var GroupChatAgentClient = class {
|
|
|
79
79
|
return this.request("POST", `/runs/${runId}/comment`, { body });
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
|
-
function buildClaudePrompt(detail) {
|
|
82
|
+
function buildClaudePrompt(detail, repoResolved) {
|
|
83
83
|
const parts = [];
|
|
84
84
|
parts.push(`# Task: ${detail.task.title}`);
|
|
85
85
|
if (detail.task.description) {
|
|
@@ -87,6 +87,19 @@ function buildClaudePrompt(detail) {
|
|
|
87
87
|
## Description
|
|
88
88
|
${detail.task.description}`);
|
|
89
89
|
}
|
|
90
|
+
if (detail.repoUrl && !repoResolved) {
|
|
91
|
+
parts.push(
|
|
92
|
+
[
|
|
93
|
+
"\n## Repository",
|
|
94
|
+
detail.repoUrl,
|
|
95
|
+
"Check if the current working directory is already this repo (compare `git remote get-url origin`).",
|
|
96
|
+
"If not, check if it exists locally in common locations like ~/Developer, ~/Projects, ~/src, or ~.",
|
|
97
|
+
"If you find it locally, `cd` into it before starting work.",
|
|
98
|
+
"If you cannot find it locally, ask the user if they would like you to clone it and where.",
|
|
99
|
+
"Do NOT clone without the user's confirmation."
|
|
100
|
+
].join("\n")
|
|
101
|
+
);
|
|
102
|
+
}
|
|
90
103
|
if (detail.prompt && detail.prompt !== detail.task.title) {
|
|
91
104
|
parts.push(`
|
|
92
105
|
## Instructions
|
|
@@ -114,6 +127,14 @@ ${comment.body}`
|
|
|
114
127
|
);
|
|
115
128
|
}
|
|
116
129
|
}
|
|
130
|
+
if (detail.lastFollowUpMessage) {
|
|
131
|
+
parts.push(
|
|
132
|
+
`
|
|
133
|
+
## Latest Follow-up Request
|
|
134
|
+
The user has sent a follow-up message. Address this:
|
|
135
|
+
${detail.lastFollowUpMessage}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
117
138
|
const existingPrUrl = detail.pullRequestUrl ?? detail.task.pullRequestUrl;
|
|
118
139
|
if (existingPrUrl) {
|
|
119
140
|
parts.push(`
|
|
@@ -600,6 +621,14 @@ function runShellCommand(cmd, args, cwd) {
|
|
|
600
621
|
});
|
|
601
622
|
}
|
|
602
623
|
var sessionCache = /* @__PURE__ */ new Map();
|
|
624
|
+
var activeProcesses = /* @__PURE__ */ new Map();
|
|
625
|
+
var stoppedRunIds = /* @__PURE__ */ new Set();
|
|
626
|
+
function stopActiveProcess(runId) {
|
|
627
|
+
const child = activeProcesses.get(runId);
|
|
628
|
+
if (!child || child.killed) return;
|
|
629
|
+
stoppedRunIds.add(runId);
|
|
630
|
+
child.kill("SIGINT");
|
|
631
|
+
}
|
|
603
632
|
var runCounter = 0;
|
|
604
633
|
var claudeRunnerSignalTeardownDone = false;
|
|
605
634
|
function tryBeginClaudeRunnerSignalTeardown() {
|
|
@@ -904,18 +933,35 @@ function truncateClaudeDebugString(text) {
|
|
|
904
933
|
truncated: true
|
|
905
934
|
};
|
|
906
935
|
}
|
|
907
|
-
async function processRun(client, run, config, worktreeDir, detail) {
|
|
936
|
+
async function processRun(client, run, config, worktreeDir, detail, runBaseDir, repoFound) {
|
|
908
937
|
const runNum = ++runCounter;
|
|
909
938
|
const runTag = ` ${C.pid}[${runNum}]${C.reset}`;
|
|
910
939
|
const log = (msg) => console.log(`${runTag} ${msg}`);
|
|
911
940
|
const logGreen = (msg) => console.log(`${runTag} ${C.green}${msg}${C.reset}`);
|
|
912
941
|
const ownerName = run.owner?.name ?? detail.owner?.name ?? "unknown";
|
|
913
942
|
log(`\u{1F4CB} "${run.taskTitle}" \u2014 delegated by ${ownerName}`);
|
|
943
|
+
if (config.verbose) {
|
|
944
|
+
log(`\u{1F4CE} Run ID: ${run.id} | Task ID: ${run.taskId}`);
|
|
945
|
+
log(
|
|
946
|
+
`\u{1F4CE} Detail status: ${detail.status} | Has lastFollowUpMessage: ${!!detail.lastFollowUpMessage} | Has lastSessionId: ${!!detail.lastSessionId}`
|
|
947
|
+
);
|
|
948
|
+
log(
|
|
949
|
+
`\u{1F4CE} In-memory session cache hit: ${sessionCache.has(run.taskId)} | Activity items: ${detail.activity.length}`
|
|
950
|
+
);
|
|
951
|
+
if (detail.lastFollowUpMessage) {
|
|
952
|
+
log(
|
|
953
|
+
`\u{1F4CE} lastFollowUpMessage: "${detail.lastFollowUpMessage.slice(0, 150)}${detail.lastFollowUpMessage.length > 150 ? "\u2026" : ""}"`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
if (detail.lastSessionId) {
|
|
957
|
+
log(`\u{1F4CE} Server lastSessionId: ${detail.lastSessionId.slice(0, 12)}\u2026`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
914
960
|
if (detail.status !== "PENDING") {
|
|
915
961
|
log(`\u23ED Run is no longer PENDING (now ${detail.status}), skipping.`);
|
|
916
962
|
return;
|
|
917
963
|
}
|
|
918
|
-
const prompt = buildClaudePrompt(detail);
|
|
964
|
+
const prompt = buildClaudePrompt(detail, !!repoFound);
|
|
919
965
|
if (config.dryRun) {
|
|
920
966
|
log("\u{1F3DC} DRY RUN \u2014 would execute Claude Code with prompt:");
|
|
921
967
|
console.log("---");
|
|
@@ -937,7 +983,7 @@ async function processRun(client, run, config, worktreeDir, detail) {
|
|
|
937
983
|
throw err;
|
|
938
984
|
}
|
|
939
985
|
log("\u25B6 Run started");
|
|
940
|
-
const effectiveCwd = worktreeDir ?? config.workDir;
|
|
986
|
+
const effectiveCwd = worktreeDir ?? runBaseDir ?? config.workDir;
|
|
941
987
|
let lastClaude = {
|
|
942
988
|
stderr: "",
|
|
943
989
|
stdout: "",
|
|
@@ -954,16 +1000,43 @@ async function processRun(client, run, config, worktreeDir, detail) {
|
|
|
954
1000
|
});
|
|
955
1001
|
await new Promise((resolve) => git.on("close", () => resolve()));
|
|
956
1002
|
}
|
|
957
|
-
const cachedSessionId = sessionCache.get(run.taskId);
|
|
1003
|
+
const cachedSessionId = sessionCache.get(run.taskId) ?? detail.lastSessionId;
|
|
958
1004
|
const isFollowUp = cachedSessionId !== void 0;
|
|
959
|
-
|
|
1005
|
+
let effectivePrompt;
|
|
1006
|
+
if (isFollowUp && detail.lastFollowUpMessage) {
|
|
1007
|
+
effectivePrompt = detail.lastFollowUpMessage;
|
|
1008
|
+
} else if (isFollowUp) {
|
|
1009
|
+
effectivePrompt = prompt;
|
|
1010
|
+
} else {
|
|
1011
|
+
effectivePrompt = prompt;
|
|
1012
|
+
}
|
|
960
1013
|
const resumeSession = isFollowUp ? cachedSessionId : void 0;
|
|
961
1014
|
if (config.verbose) {
|
|
962
1015
|
if (isFollowUp) {
|
|
963
|
-
|
|
1016
|
+
const sessionSource = sessionCache.has(run.taskId) ? "in-memory cache" : "server (lastSessionId)";
|
|
1017
|
+
log(`\u{1F504} Resuming session ${cachedSessionId.slice(0, 12)}\u2026 (source: ${sessionSource})`);
|
|
1018
|
+
if (detail.lastFollowUpMessage) {
|
|
1019
|
+
log(
|
|
1020
|
+
`\u{1F4AC} Follow-up message: "${detail.lastFollowUpMessage.slice(0, 200)}${detail.lastFollowUpMessage.length > 200 ? "\u2026" : ""}"`
|
|
1021
|
+
);
|
|
1022
|
+
} else {
|
|
1023
|
+
log(`\u26A0 No lastFollowUpMessage found \u2014 using full prompt for resumed session`);
|
|
1024
|
+
}
|
|
1025
|
+
} else {
|
|
1026
|
+
log(`\u{1F195} Initial run (no session to resume)`);
|
|
964
1027
|
}
|
|
965
|
-
log(
|
|
1028
|
+
log(
|
|
1029
|
+
`\u{1F4DD} Prompt (${effectivePrompt.length} chars)${isFollowUp ? " [follow-up]" : " [initial]"}`
|
|
1030
|
+
);
|
|
1031
|
+
const previewLen = 500;
|
|
1032
|
+
log(
|
|
1033
|
+
`\u{1F4DD} Prompt preview:
|
|
1034
|
+
${effectivePrompt.slice(0, previewLen)}${effectivePrompt.length > previewLen ? "\n... (truncated)" : ""}`
|
|
1035
|
+
);
|
|
966
1036
|
if (effectiveModel) log(`\u{1F9E0} Model: ${effectiveModel}`);
|
|
1037
|
+
log(
|
|
1038
|
+
`\u{1F4CA} Activity items: ${detail.activity.length}, Comments: ${detail.activity.filter((a) => a.type === "comment").length}`
|
|
1039
|
+
);
|
|
967
1040
|
}
|
|
968
1041
|
const { process: child, output } = spawnClaudeCode(
|
|
969
1042
|
effectivePrompt,
|
|
@@ -973,8 +1046,19 @@ async function processRun(client, run, config, worktreeDir, detail) {
|
|
|
973
1046
|
effectiveCwd
|
|
974
1047
|
);
|
|
975
1048
|
log(`\u{1F916} Claude Code spawned (pid ${child.pid})${isFollowUp ? " (follow-up)" : ""}`);
|
|
1049
|
+
activeProcesses.set(run.id, child);
|
|
1050
|
+
if (stoppedRunIds.has(run.id)) {
|
|
1051
|
+
child.kill("SIGINT");
|
|
1052
|
+
}
|
|
976
1053
|
let { stdout, rawOutput, stderr, exitCode, sessionId, streamPrUrl } = await output;
|
|
1054
|
+
activeProcesses.delete(run.id);
|
|
977
1055
|
lastClaude = { stderr, stdout, rawOutput, exitCode, sessionId };
|
|
1056
|
+
if (stoppedRunIds.has(run.id)) {
|
|
1057
|
+
stoppedRunIds.delete(run.id);
|
|
1058
|
+
if (sessionId) sessionCache.set(run.taskId, sessionId);
|
|
1059
|
+
log(`\u{1F6D1} Run stopped from UI`);
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
978
1062
|
if (exitCode !== 0 && isFollowUp) {
|
|
979
1063
|
log(`\u26A0 Session resume failed, retrying with fresh session\u2026`);
|
|
980
1064
|
sessionCache.delete(run.taskId);
|
|
@@ -985,6 +1069,9 @@ async function processRun(client, run, config, worktreeDir, detail) {
|
|
|
985
1069
|
}
|
|
986
1070
|
if (sessionId) {
|
|
987
1071
|
sessionCache.set(run.taskId, sessionId);
|
|
1072
|
+
if (config.verbose) {
|
|
1073
|
+
log(`\u{1F4BE} Session ${sessionId.slice(0, 12)}\u2026 cached for task ${run.taskId}`);
|
|
1074
|
+
}
|
|
988
1075
|
}
|
|
989
1076
|
const pullRequestUrl = streamPrUrl ?? await detectPullRequestUrl(effectiveCwd) ?? extractPullRequestUrlFromOutput(stdout) ?? extractPullRequestUrlFromOutput(rawOutput);
|
|
990
1077
|
const streamEvents = parseClaudeNdjsonEvents(rawOutput);
|
|
@@ -1019,7 +1106,8 @@ async function processRun(client, run, config, worktreeDir, detail) {
|
|
|
1019
1106
|
await client.errorRun(run.id, errorMsg, {
|
|
1020
1107
|
pullRequestUrl,
|
|
1021
1108
|
errorType: retryInfo?.errorType,
|
|
1022
|
-
retryAfter: retryInfo?.retryAfterMs
|
|
1109
|
+
retryAfter: retryInfo?.retryAfterMs,
|
|
1110
|
+
sessionId: lastClaude.sessionId
|
|
1023
1111
|
});
|
|
1024
1112
|
if (retryInfo) {
|
|
1025
1113
|
const retryAt = new Date(retryInfo.retryAfterMs).toLocaleString(void 0, {
|
|
@@ -1050,7 +1138,11 @@ async function processRun(client, run, config, worktreeDir, detail) {
|
|
|
1050
1138
|
}
|
|
1051
1139
|
const resultText = extractResultText(stdout);
|
|
1052
1140
|
const cost = extractCost(stdout);
|
|
1053
|
-
await client.completeRun(run.id, resultText, {
|
|
1141
|
+
await client.completeRun(run.id, resultText, {
|
|
1142
|
+
...cost,
|
|
1143
|
+
pullRequestUrl,
|
|
1144
|
+
sessionId: lastClaude.sessionId
|
|
1145
|
+
});
|
|
1054
1146
|
if (pullRequestUrl) logGreen(`\u{1F517} PR: ${pullRequestUrl}`);
|
|
1055
1147
|
logGreen(`\u2705 Run completed`);
|
|
1056
1148
|
} catch (err) {
|
|
@@ -1082,7 +1174,7 @@ async function processRun(client, run, config, worktreeDir, detail) {
|
|
|
1082
1174
|
${message.slice(0, 2e3)}
|
|
1083
1175
|
\`\`\``;
|
|
1084
1176
|
try {
|
|
1085
|
-
await client.errorRun(run.id, errorBody);
|
|
1177
|
+
await client.errorRun(run.id, errorBody, { sessionId: lastClaude.sessionId });
|
|
1086
1178
|
} catch {
|
|
1087
1179
|
log(`${C.dim}\u26A0 Failed to report error to API${C.reset}`);
|
|
1088
1180
|
}
|
|
@@ -1113,6 +1205,66 @@ function loadEnvFile() {
|
|
|
1113
1205
|
}
|
|
1114
1206
|
}
|
|
1115
1207
|
}
|
|
1208
|
+
var repoRegistry = /* @__PURE__ */ new Map();
|
|
1209
|
+
function normalizeRepoUrl(url) {
|
|
1210
|
+
return url.replace(/\.git$/, "").replace(/^git@github\.com:/, "https://github.com/").toLowerCase();
|
|
1211
|
+
}
|
|
1212
|
+
function gitRemoteUrl(dir) {
|
|
1213
|
+
try {
|
|
1214
|
+
return execFileSync("git", ["remote", "get-url", "origin"], {
|
|
1215
|
+
cwd: dir,
|
|
1216
|
+
encoding: "utf-8",
|
|
1217
|
+
stdio: "pipe"
|
|
1218
|
+
}).trim();
|
|
1219
|
+
} catch {
|
|
1220
|
+
return void 0;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
function dirMatchesRepo(dir, repoUrl) {
|
|
1224
|
+
const remote = gitRemoteUrl(dir);
|
|
1225
|
+
if (!remote) return false;
|
|
1226
|
+
return normalizeRepoUrl(remote) === normalizeRepoUrl(repoUrl);
|
|
1227
|
+
}
|
|
1228
|
+
function scanForRepo(repoUrl) {
|
|
1229
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
1230
|
+
if (!home) return void 0;
|
|
1231
|
+
const searchDirs = ["Developer", "Projects", "src", "dev", "code", "repos"].map(
|
|
1232
|
+
(d) => path.join(home, d)
|
|
1233
|
+
);
|
|
1234
|
+
for (const searchDir of searchDirs) {
|
|
1235
|
+
let entries;
|
|
1236
|
+
try {
|
|
1237
|
+
entries = readdirSync(searchDir);
|
|
1238
|
+
} catch {
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
for (const entry of entries) {
|
|
1242
|
+
if (entry.startsWith(".")) continue;
|
|
1243
|
+
const candidate = path.join(searchDir, entry);
|
|
1244
|
+
try {
|
|
1245
|
+
if (!statSync(candidate).isDirectory()) continue;
|
|
1246
|
+
} catch {
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
if (dirMatchesRepo(candidate, repoUrl)) return candidate;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return void 0;
|
|
1253
|
+
}
|
|
1254
|
+
function resolveRepoCwd(repoUrl, currentWorkDir) {
|
|
1255
|
+
const cached = repoRegistry.get(normalizeRepoUrl(repoUrl));
|
|
1256
|
+
if (cached) return cached;
|
|
1257
|
+
if (dirMatchesRepo(currentWorkDir, repoUrl)) {
|
|
1258
|
+
repoRegistry.set(normalizeRepoUrl(repoUrl), currentWorkDir);
|
|
1259
|
+
return currentWorkDir;
|
|
1260
|
+
}
|
|
1261
|
+
const found = scanForRepo(repoUrl);
|
|
1262
|
+
if (found) {
|
|
1263
|
+
repoRegistry.set(normalizeRepoUrl(repoUrl), found);
|
|
1264
|
+
return found;
|
|
1265
|
+
}
|
|
1266
|
+
return void 0;
|
|
1267
|
+
}
|
|
1116
1268
|
function getRunnerPackageInfo() {
|
|
1117
1269
|
try {
|
|
1118
1270
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -1327,12 +1479,21 @@ async function processRunWithDrain(client, run, scheduler, config) {
|
|
|
1327
1479
|
drainSchedulerSlot(scheduler, run);
|
|
1328
1480
|
return;
|
|
1329
1481
|
}
|
|
1482
|
+
let runBaseDir = config.workDir;
|
|
1483
|
+
let repoFound = false;
|
|
1484
|
+
if (detail.repoUrl) {
|
|
1485
|
+
const resolved = resolveRepoCwd(detail.repoUrl, config.workDir);
|
|
1486
|
+
if (resolved) {
|
|
1487
|
+
runBaseDir = resolved;
|
|
1488
|
+
repoFound = true;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1330
1491
|
let worktreeDir;
|
|
1331
|
-
if (config.useWorktrees) {
|
|
1492
|
+
if (config.useWorktrees && (!detail.repoUrl || repoFound)) {
|
|
1332
1493
|
try {
|
|
1333
|
-
const existingBranch = await resolveExistingPrBranch(detail,
|
|
1334
|
-
worktreeDir = createWorktree(
|
|
1335
|
-
const rel = path.relative(
|
|
1494
|
+
const existingBranch = await resolveExistingPrBranch(detail, runBaseDir);
|
|
1495
|
+
worktreeDir = createWorktree(runBaseDir, run.taskId, existingBranch);
|
|
1496
|
+
const rel = path.relative(runBaseDir, worktreeDir);
|
|
1336
1497
|
if (existingBranch) {
|
|
1337
1498
|
console.log(` \u{1F333} Worktree from existing PR branch ${existingBranch} \u2192 ${rel}`);
|
|
1338
1499
|
} else {
|
|
@@ -1346,7 +1507,7 @@ async function processRunWithDrain(client, run, scheduler, config) {
|
|
|
1346
1507
|
let currentDetail = detail;
|
|
1347
1508
|
while (current) {
|
|
1348
1509
|
try {
|
|
1349
|
-
await processRun(client, current, config, worktreeDir, currentDetail);
|
|
1510
|
+
await processRun(client, current, config, worktreeDir, currentDetail, runBaseDir, repoFound);
|
|
1350
1511
|
} catch (err) {
|
|
1351
1512
|
console.error(`Unhandled error processing run ${current.id}:`, err);
|
|
1352
1513
|
}
|
|
@@ -1363,14 +1524,19 @@ async function processRunWithDrain(client, run, scheduler, config) {
|
|
|
1363
1524
|
}
|
|
1364
1525
|
}
|
|
1365
1526
|
if (worktreeDir) {
|
|
1366
|
-
sessionCache.delete(run.taskId);
|
|
1367
1527
|
try {
|
|
1368
|
-
removeWorktreeSimple(
|
|
1528
|
+
removeWorktreeSimple(runBaseDir, worktreeDir);
|
|
1369
1529
|
console.log(` \u{1F9F9} Worktree cleaned up`);
|
|
1370
1530
|
} catch (err) {
|
|
1371
1531
|
console.error(` \u26A0 Failed to clean up worktree:`, err);
|
|
1372
1532
|
}
|
|
1373
1533
|
}
|
|
1534
|
+
if (detail.repoUrl && !repoFound) {
|
|
1535
|
+
const found = resolveRepoCwd(detail.repoUrl, config.workDir);
|
|
1536
|
+
if (found) {
|
|
1537
|
+
console.log(` \u{1F4C2} Repo cached for future runs: ${found}`);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1374
1540
|
}
|
|
1375
1541
|
async function runWithWebSocket(client, config, scheduler) {
|
|
1376
1542
|
let ConvexClient;
|
|
@@ -1395,6 +1561,30 @@ async function runWithWebSocket(client, config, scheduler) {
|
|
|
1395
1561
|
handlePendingRuns(runs, scheduler, client, config);
|
|
1396
1562
|
}
|
|
1397
1563
|
);
|
|
1564
|
+
let stoppedRunsWarned = false;
|
|
1565
|
+
convex.onUpdate(
|
|
1566
|
+
anyApi.agentWebSocket.stoppedRuns,
|
|
1567
|
+
{ token: config.token },
|
|
1568
|
+
(runIds) => {
|
|
1569
|
+
if (!runIds) return;
|
|
1570
|
+
for (const runId of runIds) {
|
|
1571
|
+
if (activeProcesses.has(runId)) {
|
|
1572
|
+
console.log(` \u{1F6D1} Run stopped from UI \u2014 sending stop signal to Claude Code\u2026`);
|
|
1573
|
+
stopActiveProcess(runId);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
},
|
|
1577
|
+
() => {
|
|
1578
|
+
if (!stoppedRunsWarned) {
|
|
1579
|
+
stoppedRunsWarned = true;
|
|
1580
|
+
if (config.verbose) {
|
|
1581
|
+
console.log(
|
|
1582
|
+
`${C.dim}\u2139 stoppedRuns query not available on server \u2014 stop-from-UI disabled${C.reset}`
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
);
|
|
1398
1588
|
await new Promise((resolve) => {
|
|
1399
1589
|
function shutdown() {
|
|
1400
1590
|
if (!tryBeginClaudeRunnerSignalTeardown()) return;
|
|
@@ -1444,6 +1634,16 @@ async function runWithPolling(client, config, scheduler) {
|
|
|
1444
1634
|
return;
|
|
1445
1635
|
}
|
|
1446
1636
|
while (running) {
|
|
1637
|
+
for (const runId of activeProcesses.keys()) {
|
|
1638
|
+
try {
|
|
1639
|
+
const detail = await client.getRunDetail(runId);
|
|
1640
|
+
if (detail.status === "STOPPED") {
|
|
1641
|
+
console.log(` \u{1F6D1} Run stopped from UI \u2014 sending stop signal to Claude Code\u2026`);
|
|
1642
|
+
stopActiveProcess(runId);
|
|
1643
|
+
}
|
|
1644
|
+
} catch {
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1447
1647
|
try {
|
|
1448
1648
|
const pending = await client.listPendingRuns();
|
|
1449
1649
|
handlePendingRuns(pending, scheduler, client, config);
|