hillclimb 0.9.0 → 0.9.1

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/main.js +56 -17
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -16831,7 +16831,7 @@ async function uploadArtifact(client, contributionId, filename, mimeType, buffer
16831
16831
  }
16832
16832
 
16833
16833
  // package.json
16834
- var version = "0.9.0";
16834
+ var version = "0.9.1";
16835
16835
 
16836
16836
  // src/version.ts
16837
16837
  var CLI_VERSION = version;
@@ -16973,13 +16973,14 @@ function isPhantomClaudeHook(payload, toolOverride) {
16973
16973
  var MAX_FORK_SWEEP_THREADS = 8;
16974
16974
  async function runUploadInner(payload, options = {}) {
16975
16975
  const outcome = await runUploadForSession(payload, options);
16976
- if (!options.sweptFrom && payload.tool === "codex" && (outcome === "uploaded" || outcome === "skipped")) {
16976
+ const settled = outcome === "uploaded" || outcome === "unchanged" || outcome === "skipped";
16977
+ if (!options.sweptFrom && payload.tool === "codex" && settled) {
16977
16978
  await sweepCodexForkTranscripts(payload);
16978
16979
  }
16979
- if (!options.sweptFrom && payload.tool === "claude" && (outcome === "uploaded" || outcome === "skipped")) {
16980
+ if (!options.sweptFrom && payload.tool === "claude" && settled) {
16980
16981
  await sweepClaudeSubagentTranscripts(payload);
16981
16982
  }
16982
- if (!options.sweptFrom && (outcome === "uploaded" || outcome === "skipped")) {
16983
+ if (!options.sweptFrom && settled) {
16983
16984
  await sweepStrandedAgentUploads(resolveHookSessionId(payload));
16984
16985
  }
16985
16986
  return outcome;
@@ -17020,7 +17021,13 @@ async function sweepClaudeSubagentTranscripts(payload) {
17020
17021
  const project = await findProjectForCwd(cwd);
17021
17022
  if (!project) return;
17022
17023
  files.sort((a, b) => b.modifiedMs - a.modifiedMs);
17023
- const tally = { uploaded: 0, pending: 0, skipped: 0, failed: 0 };
17024
+ const tally = {
17025
+ uploaded: 0,
17026
+ unchanged: 0,
17027
+ pending: 0,
17028
+ skipped: 0,
17029
+ failed: 0
17030
+ };
17024
17031
  for (const entry of files.slice(0, MAX_SUBAGENT_SWEEP)) {
17025
17032
  const subagentSessionId = `agent-${entry.agentId}`;
17026
17033
  try {
@@ -17039,6 +17046,7 @@ async function sweepClaudeSubagentTranscripts(payload) {
17039
17046
  }
17040
17047
  );
17041
17048
  if (outcome === "uploaded") tally.uploaded++;
17049
+ else if (outcome === "unchanged") tally.unchanged++;
17042
17050
  else if (outcome === "pending") tally.pending++;
17043
17051
  else tally.skipped++;
17044
17052
  appendLog(
@@ -17055,7 +17063,7 @@ async function sweepClaudeSubagentTranscripts(payload) {
17055
17063
  }
17056
17064
  appendLog(
17057
17065
  "info",
17058
- `[${parentSessionId}] subagent sweep (found=${files.length}, uploaded=${tally.uploaded}, pending=${tally.pending}, skipped=${tally.skipped}, failed=${tally.failed})`
17066
+ `[${parentSessionId}] subagent sweep (found=${files.length}, uploaded=${tally.uploaded}, unchanged=${tally.unchanged}, pending=${tally.pending}, skipped=${tally.skipped}, failed=${tally.failed})`
17059
17067
  );
17060
17068
  } catch (err) {
17061
17069
  appendLog(
@@ -17178,7 +17186,13 @@ async function sweepCodexForkTranscripts(payload) {
17178
17186
  registeredRoots
17179
17187
  });
17180
17188
  if (discovery.forks.length === 0) return;
17181
- const tally = { uploaded: 0, pending: 0, skipped: 0, failed: 0 };
17189
+ const tally = {
17190
+ uploaded: 0,
17191
+ unchanged: 0,
17192
+ pending: 0,
17193
+ skipped: 0,
17194
+ failed: 0
17195
+ };
17182
17196
  for (const fork of discovery.forks.slice(0, MAX_FORK_SWEEP_THREADS)) {
17183
17197
  const project = await findProjectForCwd(fork.cwd) ?? parent;
17184
17198
  const forkPayload = {
@@ -17194,6 +17208,7 @@ async function sweepCodexForkTranscripts(payload) {
17194
17208
  sweptFrom: parentSessionId
17195
17209
  });
17196
17210
  if (outcome === "uploaded") tally.uploaded++;
17211
+ else if (outcome === "unchanged") tally.unchanged++;
17197
17212
  else if (outcome === "pending") tally.pending++;
17198
17213
  else tally.skipped++;
17199
17214
  appendLog(
@@ -17210,7 +17225,7 @@ async function sweepCodexForkTranscripts(payload) {
17210
17225
  }
17211
17226
  appendLog(
17212
17227
  "info",
17213
- `[${parentSessionId}] fork sweep (scannedRollouts=${discovery.scannedRollouts}, forks=${discovery.forks.length}, uploaded=${tally.uploaded}, pending=${tally.pending}, skipped=${tally.skipped}, failed=${tally.failed})`
17228
+ `[${parentSessionId}] fork sweep (scannedRollouts=${discovery.scannedRollouts}, forks=${discovery.forks.length}, uploaded=${tally.uploaded}, unchanged=${tally.unchanged}, pending=${tally.pending}, skipped=${tally.skipped}, failed=${tally.failed})`
17214
17229
  );
17215
17230
  } catch (err) {
17216
17231
  appendLog(
@@ -17219,6 +17234,13 @@ async function sweepCodexForkTranscripts(payload) {
17219
17234
  );
17220
17235
  }
17221
17236
  }
17237
+ async function settledOutcome(before, drained, repoRoot, sourceTool, sessionId) {
17238
+ if (!drained) return "pending";
17239
+ const after = await readUploadState(repoRoot, sourceTool, sessionId);
17240
+ if (!after) return "unchanged";
17241
+ if (!before) return "uploaded";
17242
+ return after.uploadCount !== before.uploadCount || after.lastUploadedAt !== before.lastUploadedAt ? "uploaded" : "unchanged";
17243
+ }
17222
17244
  async function runUploadForSession(payload, options) {
17223
17245
  if (isPhantomClaudeHook(payload)) {
17224
17246
  appendLog("info", "upload: skipping phantom Claude hook fired by Cursor");
@@ -17265,7 +17287,8 @@ async function runUploadForSession(payload, options) {
17265
17287
  "info",
17266
17288
  `[${sessionId}] replaying ${pending.length} pending capture(s) without reading the live transcript`
17267
17289
  );
17268
- return await drainCapturedSessions({
17290
+ const before2 = await readUploadState(repoRoot, sourceTool, sessionId);
17291
+ const drained2 = await drainCapturedSessions({
17269
17292
  sessionId,
17270
17293
  repoRoot,
17271
17294
  config,
@@ -17273,7 +17296,8 @@ async function runUploadForSession(payload, options) {
17273
17296
  eventKind: eventKind2,
17274
17297
  recordedAt,
17275
17298
  transcriptPath: firstPending.transcriptPath
17276
- }) ? "uploaded" : "pending";
17299
+ });
17300
+ return settledOutcome(before2, drained2, repoRoot, sourceTool, sessionId);
17277
17301
  };
17278
17302
  const transcriptPath = await resolveTranscriptPath(
17279
17303
  payload,
@@ -17330,7 +17354,8 @@ async function runUploadForSession(payload, options) {
17330
17354
  );
17331
17355
  return "skipped";
17332
17356
  }
17333
- const uploaded = await captureAndUploadSession({
17357
+ const before = await readUploadState(repoRoot, sourceTool, sessionId);
17358
+ const drained = await captureAndUploadSession({
17334
17359
  sessionId,
17335
17360
  transcriptPath: transcriptResolved,
17336
17361
  repoRoot,
@@ -17340,7 +17365,7 @@ async function runUploadForSession(payload, options) {
17340
17365
  recordedAt,
17341
17366
  subagent: options.subagent
17342
17367
  });
17343
- return uploaded ? "uploaded" : "pending";
17368
+ return settledOutcome(before, drained, repoRoot, sourceTool, sessionId);
17344
17369
  }
17345
17370
  var AGENT_MAX_UPLOAD_BYTES = 1024 * 1024 * 1024;
17346
17371
  function makeTranscriptGroup(repoRoot, sourceTool, transcriptPath, content) {
@@ -20402,6 +20427,7 @@ async function sweepCodexForkCaptures(params) {
20402
20427
  worktree: 0,
20403
20428
  sameTree: 0,
20404
20429
  unrelated: 0,
20430
+ deferred: 0,
20405
20431
  initialized: 0,
20406
20432
  uploaded: 0,
20407
20433
  unchanged: 0,
@@ -20409,8 +20435,14 @@ async function sweepCodexForkCaptures(params) {
20409
20435
  failed: 0,
20410
20436
  skipped: 0
20411
20437
  };
20412
- for (const fork of discovery.forks.slice(0, MAX_FORK_SWEEP_THREADS2)) {
20413
- const target = await resolveForkTarget(fork.cwd, repos);
20438
+ const targets = /* @__PURE__ */ new Map();
20439
+ const hooked = /* @__PURE__ */ new Set();
20440
+ for (const fork of discovery.forks) {
20441
+ let target = targets.get(fork.cwd);
20442
+ if (target === void 0) {
20443
+ target = await resolveForkTarget(fork.cwd, repos);
20444
+ targets.set(fork.cwd, target);
20445
+ }
20414
20446
  if (!target) {
20415
20447
  tally.unrelated++;
20416
20448
  continue;
@@ -20419,12 +20451,19 @@ async function sweepCodexForkCaptures(params) {
20419
20451
  tally.sameTree++;
20420
20452
  continue;
20421
20453
  }
20422
- tally.worktree++;
20423
20454
  const repo = {
20424
20455
  repoRoot: target.repoRoot,
20425
20456
  config: target.config
20426
20457
  };
20427
- await propagateCodexHooks(project.repoRoot, repo.repoRoot);
20458
+ if (!hooked.has(repo.repoRoot)) {
20459
+ hooked.add(repo.repoRoot);
20460
+ await propagateCodexHooks(project.repoRoot, repo.repoRoot);
20461
+ }
20462
+ if (tally.worktree >= MAX_FORK_SWEEP_THREADS2) {
20463
+ tally.deferred++;
20464
+ continue;
20465
+ }
20466
+ tally.worktree++;
20428
20467
  const forkPayload = {
20429
20468
  session_id: fork.threadId,
20430
20469
  cwd: fork.cwd,
@@ -20508,7 +20547,7 @@ async function sweepCodexForkCaptures(params) {
20508
20547
  }
20509
20548
  appendLog(
20510
20549
  "info",
20511
- `git-traces: fork sweep (parent=${parentSessionId}, event=${event}, triggerRepo=${project.repoRoot}, scannedRollouts=${discovery.scannedRollouts}, forks=${discovery.forks.length}, worktree=${tally.worktree}, sameTree=${tally.sameTree}, unrelated=${tally.unrelated}, initialized=${tally.initialized}, uploaded=${tally.uploaded}, unchanged=${tally.unchanged}, pending=${tally.pending}, failed=${tally.failed}, skipped=${tally.skipped})`
20550
+ `git-traces: fork sweep (parent=${parentSessionId}, event=${event}, triggerRepo=${project.repoRoot}, scannedRollouts=${discovery.scannedRollouts}, forks=${discovery.forks.length}, worktree=${tally.worktree}, sameTree=${tally.sameTree}, unrelated=${tally.unrelated}, initialized=${tally.initialized}, uploaded=${tally.uploaded}, unchanged=${tally.unchanged}, pending=${tally.pending}, failed=${tally.failed}, skipped=${tally.skipped}, deferred=${tally.deferred})`
20512
20551
  );
20513
20552
  }
20514
20553
  async function sweepCodexForksSafely(params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hillclimb",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Extract and export AI coding tool logs grouped by repo",
5
5
  "license": "MIT",
6
6
  "type": "module",