@gethmy/agent 1.11.1 → 1.11.2

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 (3) hide show
  1. package/dist/cli.js +62 -15
  2. package/dist/index.js +62 -15
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1228,7 +1228,8 @@ function fetchBaseBranch(repoRoot, baseBranch, attempts = 3, fetchImpl = (root,
1228
1228
  log.warn(TAG5, `fetch origin ${baseBranch} failed (attempt ${attempt}/${attempts})`);
1229
1229
  }
1230
1230
  }
1231
- const detail = lastErr instanceof Error ? lastErr.message : String(lastErr);
1231
+ const e = lastErr;
1232
+ const detail = e?.stderr?.toString?.().trim() || (lastErr instanceof Error ? lastErr.message : String(lastErr));
1232
1233
  throw new WorktreeBaseError(`Could not fetch origin/${baseBranch} after ${attempts} attempts — ` + `refusing to build on a stale base. ${detail}`);
1233
1234
  }
1234
1235
  function createWorktree(basePath, baseBranch, branchName) {
@@ -1350,6 +1351,16 @@ var init_worktree = __esm(() => {
1350
1351
  import { execFileSync as execFileSync4, execSync as execSync3 } from "node:child_process";
1351
1352
  import { existsSync as existsSync3 } from "node:fs";
1352
1353
  import { resolve as resolve2 } from "node:path";
1354
+ function gitErrorDetail(err) {
1355
+ const e = err;
1356
+ const stderr = e?.stderr?.toString?.().trim();
1357
+ if (stderr)
1358
+ return stderr;
1359
+ const stdout = e?.stdout?.toString?.().trim();
1360
+ if (stdout)
1361
+ return stdout;
1362
+ return err instanceof Error ? err.message : String(err);
1363
+ }
1353
1364
  function checkoutExistingBranch(basePath, branchName) {
1354
1365
  const repoRoot = execFileSync4("git", ["rev-parse", "--show-toplevel"], {
1355
1366
  encoding: "utf-8"
@@ -1370,8 +1381,8 @@ function checkoutExistingBranch(basePath, branchName) {
1370
1381
  cwd: repoRoot,
1371
1382
  stdio: "pipe"
1372
1383
  });
1373
- } catch {
1374
- throw new Error(`Failed to fetch remote branch: ${branchName}`);
1384
+ } catch (err) {
1385
+ throw new Error(`Failed to fetch remote branch ${branchName}: ${gitErrorDetail(err)}`);
1375
1386
  }
1376
1387
  try {
1377
1388
  execFileSync4("git", ["branch", "-D", branchName], {
@@ -1380,15 +1391,19 @@ function checkoutExistingBranch(basePath, branchName) {
1380
1391
  });
1381
1392
  } catch {}
1382
1393
  log.info(TAG6, `Creating review worktree: ${worktreeDir} (branch: ${branchName})`);
1383
- execFileSync4("git", [
1384
- "worktree",
1385
- "add",
1386
- "--track",
1387
- "-b",
1388
- branchName,
1389
- worktreeDir,
1390
- `origin/${branchName}`
1391
- ], { cwd: repoRoot, stdio: "pipe" });
1394
+ try {
1395
+ execFileSync4("git", [
1396
+ "worktree",
1397
+ "add",
1398
+ "--track",
1399
+ "-b",
1400
+ branchName,
1401
+ worktreeDir,
1402
+ `origin/${branchName}`
1403
+ ], { cwd: repoRoot, stdio: "pipe" });
1404
+ } catch (err) {
1405
+ throw new Error(`Failed to create review worktree for ${branchName}: ${gitErrorDetail(err)}`);
1406
+ }
1392
1407
  log.info(TAG6, "Installing dependencies in review worktree...");
1393
1408
  try {
1394
1409
  execSync3(installCommand(), {
@@ -2613,6 +2628,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
2613
2628
  reviewFindings: [],
2614
2629
  revertWarnings: []
2615
2630
  };
2631
+ commitUncommittedChanges(worktreePath, card);
2616
2632
  const hasCommits = checkHasCommits(worktreePath, config.worktree.baseBranch);
2617
2633
  if (!hasCommits) {
2618
2634
  const { maxTurnsExhausted, failureSummary } = describeNoCommitFailure(sessionStats?.cost?.numTurns ?? 0, config.claude.maxTurns);
@@ -2787,6 +2803,37 @@ function readHeadSha(worktreePath) {
2787
2803
  return null;
2788
2804
  }
2789
2805
  }
2806
+ function commitUncommittedChanges(worktreePath, card) {
2807
+ let status = "";
2808
+ try {
2809
+ status = execFileSync9("git", ["status", "--porcelain"], {
2810
+ cwd: worktreePath,
2811
+ encoding: "utf-8"
2812
+ }).trim();
2813
+ } catch (err) {
2814
+ log.warn(TAG14, `git status failed in ${worktreePath}: ${err instanceof Error ? err.message : err}`);
2815
+ return false;
2816
+ }
2817
+ if (status.length === 0)
2818
+ return false;
2819
+ const title = card.title?.trim() || "agent changes";
2820
+ const message = `#${card.short_id} ${title}`;
2821
+ try {
2822
+ execFileSync9("git", ["add", "-A"], {
2823
+ cwd: worktreePath,
2824
+ encoding: "utf-8"
2825
+ });
2826
+ execFileSync9("git", ["commit", "-m", message], {
2827
+ cwd: worktreePath,
2828
+ encoding: "utf-8"
2829
+ });
2830
+ log.warn(TAG14, `Auto-committed uncommitted worktree changes for #${card.short_id} — agent ended without committing`);
2831
+ return true;
2832
+ } catch (err) {
2833
+ log.error(TAG14, `auto-commit failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
2834
+ return false;
2835
+ }
2836
+ }
2790
2837
  function checkHasCommits(worktreePath, baseBranch) {
2791
2838
  try {
2792
2839
  const count = execFileSync9("git", ["rev-list", "--count", `origin/${baseBranch}..HEAD`], { cwd: worktreePath, encoding: "utf-8" }).trim();
@@ -7758,7 +7805,7 @@ import { resolve as resolve3 } from "node:path";
7758
7805
  function isTransientGitNetworkError(message) {
7759
7806
  return TRANSIENT_GIT_NETWORK_ERROR.test(message);
7760
7807
  }
7761
- function gitErrorDetail(err) {
7808
+ function gitErrorDetail2(err) {
7762
7809
  if (err && typeof err === "object" && "stderr" in err) {
7763
7810
  const stderr = String(err.stderr ?? "").trim();
7764
7811
  if (stderr)
@@ -7857,7 +7904,7 @@ function pruneFailedRemoteBranches(opts) {
7857
7904
  ...GIT_NETWORK_EXEC
7858
7905
  });
7859
7906
  } catch (err) {
7860
- const detail = gitErrorDetail(err);
7907
+ const detail = gitErrorDetail2(err);
7861
7908
  if (isTransientGitNetworkError(detail)) {
7862
7909
  log.debug(TAG33, `Remote branch GC skipped — remote unreachable: ${detail}`);
7863
7910
  return result;
@@ -7909,7 +7956,7 @@ function pruneFailedRemoteBranches(opts) {
7909
7956
  });
7910
7957
  result.removed.push(ref);
7911
7958
  } catch (err) {
7912
- const detail = gitErrorDetail(err);
7959
+ const detail = gitErrorDetail2(err);
7913
7960
  if (isTransientGitNetworkError(detail)) {
7914
7961
  log.debug(TAG33, `Remote branch GC interrupted — remote unreachable: ${detail}`);
7915
7962
  break;
package/dist/index.js CHANGED
@@ -1227,7 +1227,8 @@ function fetchBaseBranch(repoRoot, baseBranch, attempts = 3, fetchImpl = (root,
1227
1227
  log.warn(TAG5, `fetch origin ${baseBranch} failed (attempt ${attempt}/${attempts})`);
1228
1228
  }
1229
1229
  }
1230
- const detail = lastErr instanceof Error ? lastErr.message : String(lastErr);
1230
+ const e = lastErr;
1231
+ const detail = e?.stderr?.toString?.().trim() || (lastErr instanceof Error ? lastErr.message : String(lastErr));
1231
1232
  throw new WorktreeBaseError(`Could not fetch origin/${baseBranch} after ${attempts} attempts — ` + `refusing to build on a stale base. ${detail}`);
1232
1233
  }
1233
1234
  function createWorktree(basePath, baseBranch, branchName) {
@@ -1349,6 +1350,16 @@ var init_worktree = __esm(() => {
1349
1350
  import { execFileSync as execFileSync4, execSync as execSync3 } from "node:child_process";
1350
1351
  import { existsSync as existsSync3 } from "node:fs";
1351
1352
  import { resolve as resolve2 } from "node:path";
1353
+ function gitErrorDetail(err) {
1354
+ const e = err;
1355
+ const stderr = e?.stderr?.toString?.().trim();
1356
+ if (stderr)
1357
+ return stderr;
1358
+ const stdout = e?.stdout?.toString?.().trim();
1359
+ if (stdout)
1360
+ return stdout;
1361
+ return err instanceof Error ? err.message : String(err);
1362
+ }
1352
1363
  function checkoutExistingBranch(basePath, branchName) {
1353
1364
  const repoRoot = execFileSync4("git", ["rev-parse", "--show-toplevel"], {
1354
1365
  encoding: "utf-8"
@@ -1369,8 +1380,8 @@ function checkoutExistingBranch(basePath, branchName) {
1369
1380
  cwd: repoRoot,
1370
1381
  stdio: "pipe"
1371
1382
  });
1372
- } catch {
1373
- throw new Error(`Failed to fetch remote branch: ${branchName}`);
1383
+ } catch (err) {
1384
+ throw new Error(`Failed to fetch remote branch ${branchName}: ${gitErrorDetail(err)}`);
1374
1385
  }
1375
1386
  try {
1376
1387
  execFileSync4("git", ["branch", "-D", branchName], {
@@ -1379,15 +1390,19 @@ function checkoutExistingBranch(basePath, branchName) {
1379
1390
  });
1380
1391
  } catch {}
1381
1392
  log.info(TAG6, `Creating review worktree: ${worktreeDir} (branch: ${branchName})`);
1382
- execFileSync4("git", [
1383
- "worktree",
1384
- "add",
1385
- "--track",
1386
- "-b",
1387
- branchName,
1388
- worktreeDir,
1389
- `origin/${branchName}`
1390
- ], { cwd: repoRoot, stdio: "pipe" });
1393
+ try {
1394
+ execFileSync4("git", [
1395
+ "worktree",
1396
+ "add",
1397
+ "--track",
1398
+ "-b",
1399
+ branchName,
1400
+ worktreeDir,
1401
+ `origin/${branchName}`
1402
+ ], { cwd: repoRoot, stdio: "pipe" });
1403
+ } catch (err) {
1404
+ throw new Error(`Failed to create review worktree for ${branchName}: ${gitErrorDetail(err)}`);
1405
+ }
1391
1406
  log.info(TAG6, "Installing dependencies in review worktree...");
1392
1407
  try {
1393
1408
  execSync3(installCommand(), {
@@ -2612,6 +2627,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
2612
2627
  reviewFindings: [],
2613
2628
  revertWarnings: []
2614
2629
  };
2630
+ commitUncommittedChanges(worktreePath, card);
2615
2631
  const hasCommits = checkHasCommits(worktreePath, config.worktree.baseBranch);
2616
2632
  if (!hasCommits) {
2617
2633
  const { maxTurnsExhausted, failureSummary } = describeNoCommitFailure(sessionStats?.cost?.numTurns ?? 0, config.claude.maxTurns);
@@ -2786,6 +2802,37 @@ function readHeadSha(worktreePath) {
2786
2802
  return null;
2787
2803
  }
2788
2804
  }
2805
+ function commitUncommittedChanges(worktreePath, card) {
2806
+ let status = "";
2807
+ try {
2808
+ status = execFileSync9("git", ["status", "--porcelain"], {
2809
+ cwd: worktreePath,
2810
+ encoding: "utf-8"
2811
+ }).trim();
2812
+ } catch (err) {
2813
+ log.warn(TAG14, `git status failed in ${worktreePath}: ${err instanceof Error ? err.message : err}`);
2814
+ return false;
2815
+ }
2816
+ if (status.length === 0)
2817
+ return false;
2818
+ const title = card.title?.trim() || "agent changes";
2819
+ const message = `#${card.short_id} ${title}`;
2820
+ try {
2821
+ execFileSync9("git", ["add", "-A"], {
2822
+ cwd: worktreePath,
2823
+ encoding: "utf-8"
2824
+ });
2825
+ execFileSync9("git", ["commit", "-m", message], {
2826
+ cwd: worktreePath,
2827
+ encoding: "utf-8"
2828
+ });
2829
+ log.warn(TAG14, `Auto-committed uncommitted worktree changes for #${card.short_id} — agent ended without committing`);
2830
+ return true;
2831
+ } catch (err) {
2832
+ log.error(TAG14, `auto-commit failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
2833
+ return false;
2834
+ }
2835
+ }
2789
2836
  function checkHasCommits(worktreePath, baseBranch) {
2790
2837
  try {
2791
2838
  const count = execFileSync9("git", ["rev-list", "--count", `origin/${baseBranch}..HEAD`], { cwd: worktreePath, encoding: "utf-8" }).trim();
@@ -7757,7 +7804,7 @@ import { resolve as resolve3 } from "node:path";
7757
7804
  function isTransientGitNetworkError(message) {
7758
7805
  return TRANSIENT_GIT_NETWORK_ERROR.test(message);
7759
7806
  }
7760
- function gitErrorDetail(err) {
7807
+ function gitErrorDetail2(err) {
7761
7808
  if (err && typeof err === "object" && "stderr" in err) {
7762
7809
  const stderr = String(err.stderr ?? "").trim();
7763
7810
  if (stderr)
@@ -7856,7 +7903,7 @@ function pruneFailedRemoteBranches(opts) {
7856
7903
  ...GIT_NETWORK_EXEC
7857
7904
  });
7858
7905
  } catch (err) {
7859
- const detail = gitErrorDetail(err);
7906
+ const detail = gitErrorDetail2(err);
7860
7907
  if (isTransientGitNetworkError(detail)) {
7861
7908
  log.debug(TAG33, `Remote branch GC skipped — remote unreachable: ${detail}`);
7862
7909
  return result;
@@ -7908,7 +7955,7 @@ function pruneFailedRemoteBranches(opts) {
7908
7955
  });
7909
7956
  result.removed.push(ref);
7910
7957
  } catch (err) {
7911
- const detail = gitErrorDetail(err);
7958
+ const detail = gitErrorDetail2(err);
7912
7959
  if (isTransientGitNetworkError(detail)) {
7913
7960
  log.debug(TAG33, `Remote branch GC interrupted — remote unreachable: ${detail}`);
7914
7961
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gethmy/agent",
3
- "version": "1.11.1",
3
+ "version": "1.11.2",
4
4
  "description": "Push-based agent daemon for Harmony — watches board assignments and spawns Claude CLI workers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",