@groupchatai/claude-runner 0.4.2 → 0.4.3

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.
Files changed (2) hide show
  1. package/dist/index.js +76 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -461,15 +461,24 @@ function getDefaultBranch(repoDir) {
461
461
  return "main";
462
462
  }
463
463
  }
464
- function createWorktree(repoDir, taskId) {
464
+ function createWorktree(repoDir, taskId, existingBranch) {
465
465
  const name = worktreeNameForTask(taskId);
466
- const branchName = `agent/${name}-${Date.now()}`;
467
466
  const worktreeBase = path.join(repoDir, WORKTREE_DIR);
468
467
  const worktreePath = path.join(worktreeBase, name);
469
468
  if (existsSync(worktreePath)) {
470
469
  return worktreePath;
471
470
  }
471
+ if (existingBranch) {
472
+ try {
473
+ execGit(["fetch", "origin", existingBranch, "--quiet"], repoDir);
474
+ } catch {
475
+ }
476
+ execGit(["worktree", "add", worktreePath, `origin/${existingBranch}`], repoDir);
477
+ writeFileSync(path.join(worktreePath, ".agent-branch"), existingBranch, "utf-8");
478
+ return worktreePath;
479
+ }
472
480
  const baseBranch = getDefaultBranch(repoDir);
481
+ const branchName = `agent/${name}-${Date.now()}`;
473
482
  try {
474
483
  execGit(["fetch", "origin", baseBranch, "--quiet"], repoDir);
475
484
  } catch {
@@ -478,6 +487,39 @@ function createWorktree(repoDir, taskId) {
478
487
  writeFileSync(path.join(worktreePath, ".agent-branch"), branchName, "utf-8");
479
488
  return worktreePath;
480
489
  }
490
+ async function resolveExistingPrBranch(detail, workDir) {
491
+ try {
492
+ if (detail.branchName) return detail.branchName;
493
+ if (detail.pullRequestUrl) {
494
+ try {
495
+ const branch2 = await runShellCommand(
496
+ "gh",
497
+ ["pr", "view", detail.pullRequestUrl, "--json", "headRefName", "--jq", ".headRefName"],
498
+ workDir
499
+ );
500
+ if (branch2?.trim()) return branch2.trim();
501
+ } catch {
502
+ }
503
+ }
504
+ const prUrls = [];
505
+ for (const item of detail.activity) {
506
+ if (item.body) {
507
+ const matches = item.body.match(GITHUB_PR_URL_RE);
508
+ if (matches) prUrls.push(...matches);
509
+ }
510
+ }
511
+ const prUrl = prUrls[prUrls.length - 1];
512
+ if (!prUrl) return void 0;
513
+ const branch = await runShellCommand(
514
+ "gh",
515
+ ["pr", "view", prUrl, "--json", "headRefName", "--jq", ".headRefName"],
516
+ workDir
517
+ );
518
+ if (branch?.trim()) return branch.trim();
519
+ } catch {
520
+ }
521
+ return void 0;
522
+ }
481
523
  async function listOurWorktrees(workDir) {
482
524
  const worktreeDir = path.join(workDir, WORKTREE_DIR);
483
525
  let entries;
@@ -640,12 +682,11 @@ Removing ${safe.length} safe worktree(s)\u2026`);
640
682
  }
641
683
  console.log();
642
684
  }
643
- async function processRun(client, run, config, worktreeDir) {
685
+ async function processRun(client, run, config, worktreeDir, detail) {
644
686
  const runNum = ++runCounter;
645
687
  const runTag = ` ${C.pid}[${runNum}]${C.reset}`;
646
688
  const log = (msg) => console.log(`${runTag} ${msg}`);
647
689
  const logGreen = (msg) => console.log(`${runTag} ${C.green}${msg}${C.reset}`);
648
- const detail = await client.getRunDetail(run.id);
649
690
  const ownerName = run.owner?.name ?? detail.owner?.name ?? "unknown";
650
691
  log(`\u{1F4CB} "${run.taskTitle}" \u2014 delegated by ${ownerName}`);
651
692
  if (detail.status !== "PENDING") {
@@ -919,6 +960,12 @@ var TaskScheduler = class {
919
960
  return this.slots.size === 0;
920
961
  }
921
962
  };
963
+ function drainSchedulerSlot(scheduler, run) {
964
+ let current = run;
965
+ while (current) {
966
+ current = scheduler.complete(current);
967
+ }
968
+ }
922
969
  function handlePendingRuns(runs, scheduler, client, config) {
923
970
  if (runs.length > 0 && config.verbose) {
924
971
  console.log(`\u{1F4EC} ${runs.length} pending run(s)`);
@@ -943,27 +990,47 @@ function handlePendingRuns(runs, scheduler, client, config) {
943
990
  }
944
991
  }
945
992
  async function processRunWithDrain(client, run, scheduler, config) {
993
+ let detail;
994
+ try {
995
+ detail = await client.getRunDetail(run.id);
996
+ } catch (err) {
997
+ console.error(` \u274C Failed to fetch run detail for ${run.id}:`, err);
998
+ drainSchedulerSlot(scheduler, run);
999
+ return;
1000
+ }
946
1001
  let worktreeDir;
947
1002
  if (config.useWorktrees) {
948
1003
  try {
949
- worktreeDir = createWorktree(config.workDir, run.taskId);
950
- console.log(
951
- ` \u{1F333} Worktree created from main \u2192 ${path.relative(config.workDir, worktreeDir)}`
952
- );
1004
+ const existingBranch = await resolveExistingPrBranch(detail, config.workDir);
1005
+ worktreeDir = createWorktree(config.workDir, run.taskId, existingBranch);
1006
+ const rel = path.relative(config.workDir, worktreeDir);
1007
+ if (existingBranch) {
1008
+ console.log(` \u{1F333} Worktree from existing PR branch ${existingBranch} \u2192 ${rel}`);
1009
+ } else {
1010
+ console.log(` \u{1F333} Worktree created from main \u2192 ${rel}`);
1011
+ }
953
1012
  } catch (err) {
954
1013
  console.error(` \u26A0 Failed to create worktree, running in-place:`, err);
955
1014
  }
956
1015
  }
957
1016
  let current = run;
1017
+ let currentDetail = detail;
958
1018
  while (current) {
959
1019
  try {
960
- await processRun(client, current, config, worktreeDir);
1020
+ await processRun(client, current, config, worktreeDir, currentDetail);
961
1021
  } catch (err) {
962
1022
  console.error(`Unhandled error processing run ${current.id}:`, err);
963
1023
  }
964
1024
  current = scheduler.complete(current);
965
1025
  if (current) {
966
1026
  console.log(` \u23ED Processing queued follow-up\u2026`);
1027
+ try {
1028
+ currentDetail = await client.getRunDetail(current.id);
1029
+ } catch (err) {
1030
+ console.error(` \u274C Failed to fetch detail for follow-up ${current.id}:`, err);
1031
+ drainSchedulerSlot(scheduler, current);
1032
+ break;
1033
+ }
967
1034
  }
968
1035
  }
969
1036
  if (worktreeDir) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groupchatai/claude-runner",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Run GroupChat AI agent tasks locally with Claude Code",
5
5
  "type": "module",
6
6
  "bin": {