@gethmy/agent 1.11.0 → 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.
- package/dist/cli.js +72 -15
- package/dist/index.js +72 -15
- 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
|
|
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
|
|
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
|
-
|
|
1384
|
-
"
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
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();
|
|
@@ -6164,6 +6211,16 @@ class Worker {
|
|
|
6164
6211
|
} catch {}
|
|
6165
6212
|
await this.recordOutcome(card.id, "failure");
|
|
6166
6213
|
} else if (this.runId && this.aborted) {
|
|
6214
|
+
try {
|
|
6215
|
+
await this.client.updateCard(card.id, { assignedAgentId: null });
|
|
6216
|
+
} catch (err) {
|
|
6217
|
+
log.warn(this.tag, `failed to release card after stop: ${err instanceof Error ? err.message : err}`);
|
|
6218
|
+
}
|
|
6219
|
+
try {
|
|
6220
|
+
await runTransition(this.client, card, { removeLabels: ["agent"] });
|
|
6221
|
+
} catch (tErr) {
|
|
6222
|
+
log.warn(this.tag, `stop label cleanup failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
|
|
6223
|
+
}
|
|
6167
6224
|
try {
|
|
6168
6225
|
await this.stateStore.endRun(this.runId, "paused", {
|
|
6169
6226
|
errorMessage: "cancelled",
|
|
@@ -7748,7 +7805,7 @@ import { resolve as resolve3 } from "node:path";
|
|
|
7748
7805
|
function isTransientGitNetworkError(message) {
|
|
7749
7806
|
return TRANSIENT_GIT_NETWORK_ERROR.test(message);
|
|
7750
7807
|
}
|
|
7751
|
-
function
|
|
7808
|
+
function gitErrorDetail2(err) {
|
|
7752
7809
|
if (err && typeof err === "object" && "stderr" in err) {
|
|
7753
7810
|
const stderr = String(err.stderr ?? "").trim();
|
|
7754
7811
|
if (stderr)
|
|
@@ -7847,7 +7904,7 @@ function pruneFailedRemoteBranches(opts) {
|
|
|
7847
7904
|
...GIT_NETWORK_EXEC
|
|
7848
7905
|
});
|
|
7849
7906
|
} catch (err) {
|
|
7850
|
-
const detail =
|
|
7907
|
+
const detail = gitErrorDetail2(err);
|
|
7851
7908
|
if (isTransientGitNetworkError(detail)) {
|
|
7852
7909
|
log.debug(TAG33, `Remote branch GC skipped — remote unreachable: ${detail}`);
|
|
7853
7910
|
return result;
|
|
@@ -7899,7 +7956,7 @@ function pruneFailedRemoteBranches(opts) {
|
|
|
7899
7956
|
});
|
|
7900
7957
|
result.removed.push(ref);
|
|
7901
7958
|
} catch (err) {
|
|
7902
|
-
const detail =
|
|
7959
|
+
const detail = gitErrorDetail2(err);
|
|
7903
7960
|
if (isTransientGitNetworkError(detail)) {
|
|
7904
7961
|
log.debug(TAG33, `Remote branch GC interrupted — remote unreachable: ${detail}`);
|
|
7905
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
|
|
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
|
|
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
|
-
|
|
1383
|
-
"
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
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();
|
|
@@ -6163,6 +6210,16 @@ class Worker {
|
|
|
6163
6210
|
} catch {}
|
|
6164
6211
|
await this.recordOutcome(card.id, "failure");
|
|
6165
6212
|
} else if (this.runId && this.aborted) {
|
|
6213
|
+
try {
|
|
6214
|
+
await this.client.updateCard(card.id, { assignedAgentId: null });
|
|
6215
|
+
} catch (err) {
|
|
6216
|
+
log.warn(this.tag, `failed to release card after stop: ${err instanceof Error ? err.message : err}`);
|
|
6217
|
+
}
|
|
6218
|
+
try {
|
|
6219
|
+
await runTransition(this.client, card, { removeLabels: ["agent"] });
|
|
6220
|
+
} catch (tErr) {
|
|
6221
|
+
log.warn(this.tag, `stop label cleanup failed on #${card.short_id}: ${tErr instanceof TransitionError ? tErr.detail : tErr}`);
|
|
6222
|
+
}
|
|
6166
6223
|
try {
|
|
6167
6224
|
await this.stateStore.endRun(this.runId, "paused", {
|
|
6168
6225
|
errorMessage: "cancelled",
|
|
@@ -7747,7 +7804,7 @@ import { resolve as resolve3 } from "node:path";
|
|
|
7747
7804
|
function isTransientGitNetworkError(message) {
|
|
7748
7805
|
return TRANSIENT_GIT_NETWORK_ERROR.test(message);
|
|
7749
7806
|
}
|
|
7750
|
-
function
|
|
7807
|
+
function gitErrorDetail2(err) {
|
|
7751
7808
|
if (err && typeof err === "object" && "stderr" in err) {
|
|
7752
7809
|
const stderr = String(err.stderr ?? "").trim();
|
|
7753
7810
|
if (stderr)
|
|
@@ -7846,7 +7903,7 @@ function pruneFailedRemoteBranches(opts) {
|
|
|
7846
7903
|
...GIT_NETWORK_EXEC
|
|
7847
7904
|
});
|
|
7848
7905
|
} catch (err) {
|
|
7849
|
-
const detail =
|
|
7906
|
+
const detail = gitErrorDetail2(err);
|
|
7850
7907
|
if (isTransientGitNetworkError(detail)) {
|
|
7851
7908
|
log.debug(TAG33, `Remote branch GC skipped — remote unreachable: ${detail}`);
|
|
7852
7909
|
return result;
|
|
@@ -7898,7 +7955,7 @@ function pruneFailedRemoteBranches(opts) {
|
|
|
7898
7955
|
});
|
|
7899
7956
|
result.removed.push(ref);
|
|
7900
7957
|
} catch (err) {
|
|
7901
|
-
const detail =
|
|
7958
|
+
const detail = gitErrorDetail2(err);
|
|
7902
7959
|
if (isTransientGitNetworkError(detail)) {
|
|
7903
7960
|
log.debug(TAG33, `Remote branch GC interrupted — remote unreachable: ${detail}`);
|
|
7904
7961
|
break;
|