hillclimb 0.9.2 → 0.9.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.
@@ -160,7 +160,7 @@ async function repoIdentity(repoRoot) {
160
160
  }
161
161
  async function logHeal(level, message) {
162
162
  try {
163
- const { appendLog: appendLog2 } = await import("./log-6OIO3F7C.js");
163
+ const { appendLog: appendLog2 } = await import("./log-UZPPBFMO.js");
164
164
  appendLog2(level, message);
165
165
  } catch {
166
166
  }
@@ -182,7 +182,7 @@ function matchByPath(projects, resolved) {
182
182
  var healMisses = /* @__PURE__ */ new Set();
183
183
  async function healRenamedProject(file, resolved) {
184
184
  const dead = Object.entries(file.projects).filter(
185
- ([key]) => !fs.existsSync(path.resolve(key))
185
+ ([key]) => pathIsGone(path.resolve(key))
186
186
  );
187
187
  if (dead.length === 0) return null;
188
188
  const missKey = `${configPath()}|${resolved}`;
@@ -288,11 +288,26 @@ async function matchLinkedWorktree(projects, resolved) {
288
288
  }
289
289
  return null;
290
290
  }
291
+ function hasNestedGitFile(root, target) {
292
+ let dir = target;
293
+ while (dir !== root && dir.startsWith(root + path.sep)) {
294
+ try {
295
+ if (fs.statSync(path.join(dir, ".git")).isFile()) return true;
296
+ } catch {
297
+ }
298
+ dir = path.dirname(dir);
299
+ }
300
+ return false;
301
+ }
291
302
  async function findProjectForCwd(cwd) {
292
303
  const file = await loadProjects();
293
304
  const resolved = path.resolve(cwd);
294
305
  const match = matchByPath(file.projects, resolved);
295
306
  if (match) {
307
+ if (hasNestedGitFile(match.repoRoot, resolved)) {
308
+ const nested = await matchLinkedWorktree(file.projects, resolved);
309
+ if (nested) return nested;
310
+ }
296
311
  await backfillIdentity(file, match);
297
312
  return { repoRoot: match.repoRoot, config: match.config };
298
313
  }
@@ -383,5 +398,6 @@ export {
383
398
  configPath,
384
399
  loadProjects,
385
400
  upsertProject,
401
+ hasNestedGitFile,
386
402
  findProjectForCwd
387
403
  };
@@ -3,7 +3,7 @@ import {
3
3
  appendLog,
4
4
  setLogPrefix,
5
5
  todayLogPath
6
- } from "./chunk-JZC2F6VA.js";
6
+ } from "./chunk-UPXMA7RC.js";
7
7
  export {
8
8
  appendLog,
9
9
  setLogPrefix,
package/dist/main.js CHANGED
@@ -5,11 +5,12 @@ import {
5
5
  configDir,
6
6
  configPath,
7
7
  findProjectForCwd,
8
+ hasNestedGitFile,
8
9
  loadProjects,
9
10
  setLogPrefix,
10
11
  todayLogPath,
11
12
  upsertProject
12
- } from "./chunk-JZC2F6VA.js";
13
+ } from "./chunk-UPXMA7RC.js";
13
14
 
14
15
  // src/main.ts
15
16
  import fs27 from "fs";
@@ -992,7 +993,7 @@ var TOOLS = [
992
993
  { eventName: "Stop", command: GIT_TRACES_CMD("claude") },
993
994
  { eventName: "SessionEnd", command: GIT_TRACES_CMD("claude") }
994
995
  ],
995
- detect: () => isDir(path4.join(os.homedir(), ".claude"))
996
+ detect: () => isDir(path4.join(homeDir(), ".claude"))
996
997
  },
997
998
  {
998
999
  tool: "cursor",
@@ -1009,7 +1010,7 @@ var TOOLS = [
1009
1010
  { eventName: "stop", command: GIT_TRACES_CMD("cursor") },
1010
1011
  { eventName: "sessionEnd", command: GIT_TRACES_CMD("cursor") }
1011
1012
  ],
1012
- detect: () => isDir(path4.join(os.homedir(), ".cursor"))
1013
+ detect: () => isDir(path4.join(homeDir(), ".cursor"))
1013
1014
  },
1014
1015
  {
1015
1016
  // opencode has no settings-file hook system (the `experimental.hook` block
@@ -1024,7 +1025,7 @@ var TOOLS = [
1024
1025
  format: "opencode",
1025
1026
  events: [],
1026
1027
  // Events are wired inside the plugin itself; see OPENCODE_PLUGIN_CONTENT.
1027
- detect: () => isDir(path4.join(os.homedir(), ".local", "share", "opencode")) || isDir(path4.join(os.homedir(), ".config", "opencode"))
1028
+ detect: () => isDir(path4.join(homeDir(), ".local", "share", "opencode")) || isDir(path4.join(homeDir(), ".config", "opencode"))
1028
1029
  },
1029
1030
  {
1030
1031
  // VS Code Copilot Chat. Same "no SessionEnd" constraint as Codex (the
@@ -1064,7 +1065,7 @@ var TOOLS = [
1064
1065
  { eventName: "SessionStart", command: GIT_TRACES_CMD("codex") },
1065
1066
  { eventName: "Stop", command: GIT_TRACES_CMD("codex") }
1066
1067
  ],
1067
- detect: () => isDir(path4.join(os.homedir(), ".codex"))
1068
+ detect: () => isDir(path4.join(homeDir(), ".codex"))
1068
1069
  }
1069
1070
  ];
1070
1071
  function isDir(p7) {
@@ -1074,8 +1075,11 @@ function isDir(p7) {
1074
1075
  return false;
1075
1076
  }
1076
1077
  }
1078
+ function homeDir() {
1079
+ return process.env.HOME || os.homedir();
1080
+ }
1077
1081
  function copilotChatDetect() {
1078
- const home = os.homedir();
1082
+ const home = homeDir();
1079
1083
  const suffix = path4.join("User", "globalStorage", "github.copilot-chat");
1080
1084
  const candidates = [
1081
1085
  path4.join(home, "Library", "Application Support", "Code", suffix),
@@ -2720,22 +2724,76 @@ import { createReadStream } from "fs";
2720
2724
  import fs11 from "fs/promises";
2721
2725
  import path10 from "path";
2722
2726
 
2727
+ // src/timing-profile.ts
2728
+ var DEFAULT_STAGE_MIN_MS = 250;
2729
+ var DEFAULT_LOCK_WAIT_MIN_MS = 1e3;
2730
+ var STAGE_MIN_MS_ENV = "HILLCLIMB_TIMING_STAGE_MIN_MS";
2731
+ var LOCK_WAIT_MIN_MS_ENV = "HILLCLIMB_TIMING_LOCK_WAIT_MIN_MS";
2732
+ function thresholdMs(name, fallback) {
2733
+ const raw = process.env[name];
2734
+ if (raw === void 0 || raw.trim() === "") return fallback;
2735
+ const value = Number(raw);
2736
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
2737
+ }
2738
+ function shouldLogStage(stage, elapsedMs, status) {
2739
+ if (stage === "lock-wait") {
2740
+ return elapsedMs >= thresholdMs(LOCK_WAIT_MIN_MS_ENV, DEFAULT_LOCK_WAIT_MIN_MS);
2741
+ }
2742
+ return status === "failed" || elapsedMs >= thresholdMs(STAGE_MIN_MS_ENV, DEFAULT_STAGE_MIN_MS);
2743
+ }
2744
+ var StageProfile = class {
2745
+ constructor(prefix) {
2746
+ this.prefix = prefix;
2747
+ }
2748
+ totals = /* @__PURE__ */ new Map();
2749
+ record(stage, elapsedMs, status) {
2750
+ const totals = this.totals.get(stage) ?? {
2751
+ count: 0,
2752
+ totalMs: 0,
2753
+ maxMs: 0,
2754
+ failed: 0
2755
+ };
2756
+ totals.count++;
2757
+ totals.totalMs += elapsedMs;
2758
+ totals.maxMs = Math.max(totals.maxMs, elapsedMs);
2759
+ if (status === "failed") totals.failed++;
2760
+ this.totals.set(stage, totals);
2761
+ }
2762
+ flush() {
2763
+ if (this.totals.size === 0) return;
2764
+ const entries = [...this.totals].map(([stage, totals]) => {
2765
+ let entry = `${stage}=${totals.totalMs}ms/${totals.count}`;
2766
+ if (totals.count > 1) entry += ` max=${totals.maxMs}ms`;
2767
+ if (totals.failed > 0) entry += ` failed=${totals.failed}`;
2768
+ return entry;
2769
+ });
2770
+ appendLog(
2771
+ "info",
2772
+ `${this.prefix} (stages=${this.totals.size}, ${entries.join(", ")})`
2773
+ );
2774
+ this.totals.clear();
2775
+ }
2776
+ };
2777
+
2723
2778
  // src/agent-timing.ts
2779
+ var profile = new StageProfile("agent-upload: timing profile");
2724
2780
  function startAgentStage(stage, context = "") {
2725
2781
  const startedAt = Date.now();
2726
2782
  const started = performance.now();
2727
2783
  const suffix = context ? `, ${context}` : "";
2728
- appendLog(
2729
- "info",
2730
- `agent-upload: timing (stage=${stage}, status=started, startedAt=${startedAt}${suffix})`
2731
- );
2732
2784
  return (status, details) => {
2785
+ const elapsedMs = Math.round(performance.now() - started);
2786
+ profile.record(stage, elapsedMs, status);
2787
+ if (!shouldLogStage(stage, elapsedMs, status)) return;
2733
2788
  appendLog(
2734
2789
  "info",
2735
- `agent-upload: timing (stage=${stage}, status=${status}, elapsedMs=${Math.round(performance.now() - started)}, finishedAt=${Date.now()}${suffix}${details ? `, ${details}` : ""})`
2790
+ `agent-upload: timing (stage=${stage}, status=${status}, elapsedMs=${elapsedMs}, startedAt=${startedAt}, finishedAt=${Date.now()}${suffix}${details ? `, ${details}` : ""})`
2736
2791
  );
2737
2792
  };
2738
2793
  }
2794
+ function flushAgentProfile() {
2795
+ profile.flush();
2796
+ }
2739
2797
  async function measureAgentStage(stage, action, context) {
2740
2798
  const finish = startAgentStage(stage, context);
2741
2799
  let status = "failed";
@@ -3199,6 +3257,47 @@ async function detectCodexLineage(childPath) {
3199
3257
  }
3200
3258
  }
3201
3259
 
3260
+ // src/git-traces/timing.ts
3261
+ import { performance as performance2 } from "perf_hooks";
3262
+ var profile2 = new StageProfile("git-traces: capture profile");
3263
+ function startCaptureStage(stage, context, startedAt = Date.now()) {
3264
+ const started = performance2.now();
3265
+ return (status, details) => {
3266
+ const elapsedMs = Math.round(performance2.now() - started);
3267
+ profile2.record(stage, elapsedMs, status);
3268
+ if (!shouldLogStage(stage, elapsedMs, status)) return;
3269
+ appendLog(
3270
+ "info",
3271
+ `git-traces: capture timing (${context}, stage=${stage}, status=${status}, elapsedMs=${elapsedMs}, startedAt=${startedAt}, finishedAt=${Date.now()}${details ? `, ${details}` : ""})`
3272
+ );
3273
+ };
3274
+ }
3275
+ function flushCaptureProfile() {
3276
+ profile2.flush();
3277
+ }
3278
+ async function timeAsyncCaptureStage(stage, context, run) {
3279
+ const finish = startCaptureStage(stage, context);
3280
+ let status = "failed";
3281
+ try {
3282
+ const result = await run();
3283
+ status = "ok";
3284
+ return result;
3285
+ } finally {
3286
+ finish(status);
3287
+ }
3288
+ }
3289
+ function timeCaptureStage(repoRoot, stage, run) {
3290
+ const finish = startCaptureStage(stage, `repo=${repoRoot}`);
3291
+ let status = "failed";
3292
+ try {
3293
+ const result = run();
3294
+ status = "ok";
3295
+ return result;
3296
+ } finally {
3297
+ finish(status);
3298
+ }
3299
+ }
3300
+
3202
3301
  // src/hook-timing.ts
3203
3302
  var HOOK_RECEIVED_AT_ENV = "HILLCLIMB_HOOK_RECEIVED_AT";
3204
3303
  function pinHookReceivedAt(env, now = Date.now()) {
@@ -3222,6 +3321,8 @@ async function measureHookWork(kind, action) {
3222
3321
  status = "completed";
3223
3322
  return result;
3224
3323
  } finally {
3324
+ flushCaptureProfile();
3325
+ flushAgentProfile();
3225
3326
  appendLog(
3226
3327
  "info",
3227
3328
  `hook-work: kind=${kind} status=${status} finishedAt=${Date.now()} workMs=${Math.round(performance.now() - started)} hookAgeMs=${Math.max(0, Date.now() - receivedAt)}`
@@ -4469,17 +4570,22 @@ async function gitCommonDir(cwd) {
4469
4570
  async function classifyForkCwd(cwd, parentRepoRoot) {
4470
4571
  const resolved = await canonicalPath(cwd);
4471
4572
  const parentRoot = await canonicalPath(parentRepoRoot);
4472
- if (isAtOrBelow(resolved, parentRoot))
4573
+ const below = isAtOrBelow(resolved, parentRoot);
4574
+ if (below && !hasNestedGitFile(parentRoot, resolved))
4473
4575
  return { kind: "same-tree", repoRoot: parentRoot };
4474
4576
  const toplevel = await gitOut(resolved, ["rev-parse", "--show-toplevel"]);
4475
- if (!toplevel) return { kind: "unrelated", repoRoot: null };
4577
+ if (!toplevel)
4578
+ return below ? { kind: "same-tree", repoRoot: parentRoot } : { kind: "unrelated", repoRoot: null };
4579
+ const worktreeRoot = await canonicalPath(toplevel);
4580
+ if (worktreeRoot === parentRoot)
4581
+ return { kind: "same-tree", repoRoot: parentRoot };
4476
4582
  const [forkCommon, parentCommon] = await Promise.all([
4477
4583
  gitCommonDir(resolved),
4478
4584
  gitCommonDir(parentRoot)
4479
4585
  ]);
4480
4586
  if (forkCommon && parentCommon && forkCommon === parentCommon)
4481
- return { kind: "worktree", repoRoot: await canonicalPath(toplevel) };
4482
- return { kind: "unrelated", repoRoot: null };
4587
+ return { kind: "worktree", repoRoot: worktreeRoot };
4588
+ return below ? { kind: "same-tree", repoRoot: parentRoot } : { kind: "unrelated", repoRoot: null };
4483
4589
  }
4484
4590
  async function discoverCodexForkThreads(params) {
4485
4591
  const nowMs = params.nowMs ?? Date.now();
@@ -14662,46 +14768,6 @@ import fs17 from "fs";
14662
14768
  import os7 from "os";
14663
14769
  import path16 from "path";
14664
14770
  import { createGzip } from "zlib";
14665
-
14666
- // src/git-traces/timing.ts
14667
- import { performance as performance2 } from "perf_hooks";
14668
- function startCaptureStage(stage, context, startedAt = Date.now()) {
14669
- const started = performance2.now();
14670
- appendLog(
14671
- "info",
14672
- `git-traces: capture timing (${context}, stage=${stage}, status=started, startedAt=${startedAt})`
14673
- );
14674
- return (status, details) => {
14675
- appendLog(
14676
- "info",
14677
- `git-traces: capture timing (${context}, stage=${stage}, status=${status}, elapsedMs=${Math.round(performance2.now() - started)}, startedAt=${startedAt}, finishedAt=${Date.now()}${details ? `, ${details}` : ""})`
14678
- );
14679
- };
14680
- }
14681
- async function timeAsyncCaptureStage(stage, context, run) {
14682
- const finish = startCaptureStage(stage, context);
14683
- let status = "failed";
14684
- try {
14685
- const result = await run();
14686
- status = "ok";
14687
- return result;
14688
- } finally {
14689
- finish(status);
14690
- }
14691
- }
14692
- function timeCaptureStage(repoRoot, stage, run) {
14693
- const finish = startCaptureStage(stage, `repo=${repoRoot}`);
14694
- let status = "failed";
14695
- try {
14696
- const result = run();
14697
- status = "ok";
14698
- return result;
14699
- } finally {
14700
- finish(status);
14701
- }
14702
- }
14703
-
14704
- // src/git-traces/git-ops.ts
14705
14771
  var DEFAULT_GIT_COMMAND_TIMEOUT_MS = 12e4;
14706
14772
  var DEFAULT_BUNDLE_TIMEOUT_MS = 3e5;
14707
14773
  var FULL_TREE_SCAN_TIMEOUT_MS = 3e5;
@@ -15042,9 +15108,11 @@ function isGitRepo(dir) {
15042
15108
  function stashRetryReason(failure) {
15043
15109
  const status = failure.gitExitStatus;
15044
15110
  if (status !== 1 && status !== 128) return null;
15045
- if (/Unable to create [^\n]*index\.lock[^\n]*: File exists/.test(
15046
- failure.gitStderr ?? ""
15047
- )) {
15111
+ const stderr = failure.gitStderr ?? "";
15112
+ if (/Unable to create [^\n]*index\.lock[^\n]*: File exists/.test(stderr)) {
15113
+ return "index-lock-contention";
15114
+ }
15115
+ if (status === 1 && stderr.trim() === "error: could not write index") {
15048
15116
  return "index-lock-contention";
15049
15117
  }
15050
15118
  if (status === 1 && !failure.gitStderr && !failure.gitStdout) {
@@ -15052,7 +15120,60 @@ function stashRetryReason(failure) {
15052
15120
  }
15053
15121
  return null;
15054
15122
  }
15123
+ var PRIVATE_INDEX_ENV = "HILLCLIMB_GIT_PRIVATE_INDEX";
15124
+ function withPrivateIndex(repoRoot, run) {
15125
+ if (process.env[PRIVATE_INDEX_ENV] === "0") return run(null);
15126
+ let tmpDir = null;
15127
+ let copy = null;
15128
+ try {
15129
+ const indexPath = path16.resolve(
15130
+ repoRoot,
15131
+ git(repoRoot, ["rev-parse", "--git-path", "index"])
15132
+ );
15133
+ tmpDir = fs17.mkdtempSync(path16.join(os7.tmpdir(), "hillclimb-index-"));
15134
+ copy = path16.join(tmpDir, "index");
15135
+ const fd = fs17.openSync(indexPath, "r");
15136
+ let stat;
15137
+ let data;
15138
+ try {
15139
+ stat = fs17.fstatSync(fd);
15140
+ data = fs17.readFileSync(fd);
15141
+ } finally {
15142
+ fs17.closeSync(fd);
15143
+ }
15144
+ fs17.writeFileSync(copy, data);
15145
+ fs17.utimesSync(copy, stat.atime, stat.mtime);
15146
+ } catch (err) {
15147
+ const missing = err.code === "ENOENT";
15148
+ appendLog(
15149
+ missing ? "info" : "warn",
15150
+ `git-traces: private index unavailable; capturing on the repository index (repo=${repoRoot}, reason=${err instanceof Error ? err.message : String(err)})`
15151
+ );
15152
+ copy = null;
15153
+ }
15154
+ try {
15155
+ return run(copy ? privateIndexEnv(copy) : null);
15156
+ } finally {
15157
+ if (tmpDir) fs17.rmSync(tmpDir, { recursive: true, force: true });
15158
+ }
15159
+ }
15160
+ function privateIndexEnv(indexPath) {
15161
+ const existing = Number(process.env.GIT_CONFIG_COUNT);
15162
+ const slot = Number.isInteger(existing) && existing > 0 ? existing : 0;
15163
+ return {
15164
+ GIT_INDEX_FILE: indexPath,
15165
+ GIT_CONFIG_COUNT: String(slot + 1),
15166
+ [`GIT_CONFIG_KEY_${slot}`]: "core.splitIndex",
15167
+ [`GIT_CONFIG_VALUE_${slot}`]: "false"
15168
+ };
15169
+ }
15055
15170
  function captureWorkingCommitSha(repoRoot) {
15171
+ return withPrivateIndex(
15172
+ repoRoot,
15173
+ (indexEnv) => captureWorkingCommitShaOnIndex(repoRoot, indexEnv)
15174
+ );
15175
+ }
15176
+ function captureWorkingCommitShaOnIndex(repoRoot, indexEnv) {
15056
15177
  for (let attempt = 1; ; attempt++) {
15057
15178
  let sha;
15058
15179
  try {
@@ -15063,6 +15184,7 @@ function captureWorkingCommitSha(repoRoot) {
15063
15184
  // Keep the two expected stderr classifications independent of locale.
15064
15185
  gitWithEnv(repoRoot, ["stash", "create"], {
15065
15186
  ...collectorIdentityEnv(),
15187
+ ...indexEnv,
15066
15188
  LC_ALL: "C"
15067
15189
  })
15068
15190
  )
@@ -15075,7 +15197,10 @@ function captureWorkingCommitSha(repoRoot) {
15075
15197
  "info",
15076
15198
  `git-traces: stash create unavailable (repo=${repoRoot}, reason=unborn-head); capturing index tree`
15077
15199
  );
15078
- const tree = git(repoRoot, ["write-tree"]);
15200
+ const tree = gitWithEnv(repoRoot, ["write-tree"], {
15201
+ ...process.env,
15202
+ ...indexEnv
15203
+ });
15079
15204
  return commitTree(repoRoot, tree, "empty baseline");
15080
15205
  }
15081
15206
  const retryReason = stashRetryReason(failure);
@@ -15777,7 +15902,10 @@ function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersio
15777
15902
  const headSha = safeGit(repoRoot, ["rev-parse", "HEAD"]) ?? "unknown";
15778
15903
  const branch = safeGit(repoRoot, ["rev-parse", "--abbrev-ref", "HEAD"]) ?? null;
15779
15904
  const remoteUrl = safeGit(repoRoot, ["config", "--get", "remote.origin.url"]) ?? null;
15780
- const statusRaw = safeGitBuffer(repoRoot, ["status", "--porcelain=v1", "-z"]) ?? Buffer.alloc(0);
15905
+ const statusRaw = withPrivateIndex(
15906
+ repoRoot,
15907
+ (indexEnv) => safeGitBuffer(repoRoot, ["status", "--porcelain=v1", "-z"], indexEnv)
15908
+ ) ?? Buffer.alloc(0);
15781
15909
  const dirtyFiles = parseDirtyFilesFromStatus(statusRaw);
15782
15910
  const authorName = safeGit(repoRoot, ["config", "user.name"]) ?? "";
15783
15911
  const authorEmail = safeGit(repoRoot, ["config", "user.email"]) ?? "";
@@ -15926,9 +16054,13 @@ function safeGit(repoRoot, args) {
15926
16054
  return null;
15927
16055
  }
15928
16056
  }
15929
- function safeGitBuffer(repoRoot, args) {
16057
+ function safeGitBuffer(repoRoot, args, env = null) {
15930
16058
  try {
15931
- return gitBuffer(repoRoot, args);
16059
+ return gitBuffer(
16060
+ repoRoot,
16061
+ args,
16062
+ env ? { env: { ...process.env, ...env } } : {}
16063
+ );
15932
16064
  } catch (err) {
15933
16065
  if (isGitTimeoutError(err)) {
15934
16066
  appendLog(
@@ -16837,7 +16969,7 @@ async function uploadArtifact(client, contributionId, filename, mimeType, buffer
16837
16969
  }
16838
16970
 
16839
16971
  // package.json
16840
- var version = "0.9.2";
16972
+ var version = "0.9.3";
16841
16973
 
16842
16974
  // src/version.ts
16843
16975
  var CLI_VERSION = version;
@@ -16978,8 +17110,14 @@ function isPhantomClaudeHook(payload, toolOverride) {
16978
17110
  }
16979
17111
  var MAX_FORK_SWEEP_THREADS = 8;
16980
17112
  async function runUploadInner(payload, options = {}) {
16981
- const outcome = await runUploadForSession(payload, options);
16982
- const settled = outcome === "uploaded" || outcome === "unchanged" || outcome === "skipped";
17113
+ let outcome;
17114
+ let failure;
17115
+ try {
17116
+ outcome = await runUploadForSession(payload, options);
17117
+ } catch (err) {
17118
+ failure = err;
17119
+ }
17120
+ const settled = failure !== void 0 || outcome === "uploaded" || outcome === "unchanged" || outcome === "skipped";
16983
17121
  if (!options.sweptFrom && payload.tool === "codex" && settled) {
16984
17122
  await sweepCodexForkTranscripts(payload);
16985
17123
  }
@@ -16989,6 +17127,7 @@ async function runUploadInner(payload, options = {}) {
16989
17127
  if (!options.sweptFrom && settled) {
16990
17128
  await sweepStrandedAgentUploads(resolveHookSessionId(payload));
16991
17129
  }
17130
+ if (failure !== void 0) throw failure;
16992
17131
  return outcome;
16993
17132
  }
16994
17133
  var MAX_SUBAGENT_SWEEP = 32;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hillclimb",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "Extract and export AI coding tool logs grouped by repo",
5
5
  "license": "MIT",
6
6
  "type": "module",