hillclimb 0.4.1 → 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/cli.js +71 -34
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -11150,7 +11150,13 @@ async function eventContext(tool, payload) {
11150
11150
  conversationId: stringOrNull(payload.conversation_id),
11151
11151
  turnId,
11152
11152
  eventNonce,
11153
- ...await transcriptFingerprint(payload)
11153
+ // Only per-turn stop events need the transcript to disambiguate (they
11154
+ // often lack a turn id); sessionEnd is keyed by sessionId. Folding it in
11155
+ // for sessionEnd would diverge the agent/git eventIds whenever the two
11156
+ // hooks send different payloads — e.g. the opencode plugin passes
11157
+ // transcript_path to the upload spawn but not the git-traces spawn,
11158
+ // causing a 60s orphan wait and a duplicate debug-log upload.
11159
+ ...eventKind === "stop" ? await transcriptFingerprint(payload) : {}
11154
11160
  };
11155
11161
  const eventId = crypto.createHash("sha256").update(JSON.stringify(fingerprint)).digest("hex").slice(0, 32);
11156
11162
  return {
@@ -13619,8 +13625,9 @@ function captureSnapshotSha(repoRoot) {
13619
13625
  }
13620
13626
  return git(repoRoot, ["rev-parse", "HEAD"]);
13621
13627
  }
13622
- function recordOmittedSnapshotFile(omittedFiles, file) {
13628
+ function recordOmittedSnapshotFile(omittedFiles, file, options = {}) {
13623
13629
  omittedFiles?.push(file);
13630
+ if (options.log === false) return;
13624
13631
  const action = file.tracked ? "omitting tracked" : "skipping untracked";
13625
13632
  appendLog(
13626
13633
  "warn",
@@ -13643,7 +13650,7 @@ function parseLsTreeLongZ(output) {
13643
13650
  }
13644
13651
  return entries;
13645
13652
  }
13646
- function listOversizedTreeFiles(repoRoot, treeSha, omittedFiles) {
13653
+ function listOversizedTreeFiles(repoRoot, treeSha, options = {}) {
13647
13654
  const output = gitBuffer(repoRoot, ["ls-tree", "-r", "-l", "-z", treeSha]);
13648
13655
  const oversized = [];
13649
13656
  for (const entry of parseLsTreeLongZ(output)) {
@@ -13656,7 +13663,9 @@ function listOversizedTreeFiles(repoRoot, treeSha, omittedFiles) {
13656
13663
  gitObjectSha: entry.sha
13657
13664
  };
13658
13665
  oversized.push(file);
13659
- recordOmittedSnapshotFile(omittedFiles, file);
13666
+ recordOmittedSnapshotFile(options.omittedFiles, file, {
13667
+ log: options.log
13668
+ });
13660
13669
  }
13661
13670
  return oversized;
13662
13671
  }
@@ -13667,6 +13676,29 @@ function removePathsFromIndex(repoRoot, env, paths) {
13667
13676
  env
13668
13677
  });
13669
13678
  }
13679
+ function filterOversizedFilesFromTree(repoRoot, treeSha, options = {}) {
13680
+ const oversizedFiles = listOversizedTreeFiles(repoRoot, treeSha, options);
13681
+ if (oversizedFiles.length === 0) return treeSha;
13682
+ const tmpIndex = path14.join(
13683
+ os6.tmpdir(),
13684
+ `hillclimb-filter-${Date.now()}-${process.pid}`
13685
+ );
13686
+ const env = { ...process.env, GIT_INDEX_FILE: tmpIndex };
13687
+ try {
13688
+ gitWithEnv(repoRoot, ["read-tree", treeSha], env);
13689
+ removePathsFromIndex(
13690
+ repoRoot,
13691
+ env,
13692
+ oversizedFiles.map((file) => file.path)
13693
+ );
13694
+ return gitWithEnv(repoRoot, ["write-tree"], env);
13695
+ } finally {
13696
+ try {
13697
+ fs11.unlinkSync(tmpIndex);
13698
+ } catch {
13699
+ }
13700
+ }
13701
+ }
13670
13702
  function buildUntrackedTree(repoRoot, omittedFiles) {
13671
13703
  const list = git(repoRoot, [
13672
13704
  "ls-files",
@@ -13714,46 +13746,40 @@ function buildUntrackedTree(repoRoot, omittedFiles) {
13714
13746
  }
13715
13747
  function buildSnapshotTree(repoRoot, stashSha, options = {}) {
13716
13748
  const trackedTree = git(repoRoot, ["rev-parse", `${stashSha}^{tree}`]);
13717
- const oversizedTracked = listOversizedTreeFiles(
13749
+ const filteredTrackedTree = filterOversizedFilesFromTree(
13718
13750
  repoRoot,
13719
13751
  trackedTree,
13720
- options.omittedFiles
13752
+ {
13753
+ omittedFiles: options.omittedFiles
13754
+ }
13721
13755
  );
13722
13756
  const untrackedTree = buildUntrackedTree(repoRoot, options.omittedFiles);
13723
- if (oversizedTracked.length === 0 && (!untrackedTree || untrackedTree === EMPTY_TREE_SHA)) {
13724
- return trackedTree;
13725
- }
13757
+ if (!untrackedTree || untrackedTree === EMPTY_TREE_SHA)
13758
+ return filteredTrackedTree;
13726
13759
  const tmpIndex = path14.join(
13727
13760
  os6.tmpdir(),
13728
13761
  `hillclimb-index-${Date.now()}-${process.pid}`
13729
13762
  );
13730
13763
  const env = { ...process.env, GIT_INDEX_FILE: tmpIndex };
13731
13764
  try {
13732
- gitWithEnv(repoRoot, ["read-tree", trackedTree], env);
13733
- removePathsFromIndex(
13765
+ gitWithEnv(repoRoot, ["read-tree", filteredTrackedTree], env);
13766
+ const untrackedList = gitBuffer(
13734
13767
  repoRoot,
13735
- env,
13736
- oversizedTracked.map((file) => file.path)
13737
- );
13738
- if (untrackedTree && untrackedTree !== EMPTY_TREE_SHA) {
13739
- const untrackedList = gitBuffer(
13740
- repoRoot,
13741
- ["ls-tree", "-r", untrackedTree],
13742
- {
13743
- env
13744
- }
13745
- ).toString("utf-8");
13746
- const indexInfo = untrackedList.split("\n").filter(Boolean).map((line) => {
13747
- const [meta, filePath] = line.split(" ");
13748
- const [mode, , sha] = meta.split(/\s+/);
13749
- return `${mode} ${sha} ${filePath}`;
13750
- }).join("\n");
13751
- if (indexInfo) {
13752
- gitBuffer(repoRoot, ["update-index", "--index-info"], {
13753
- input: indexInfo,
13754
- env
13755
- });
13768
+ ["ls-tree", "-r", untrackedTree],
13769
+ {
13770
+ env
13756
13771
  }
13772
+ ).toString("utf-8");
13773
+ const indexInfo = untrackedList.split("\n").filter(Boolean).map((line) => {
13774
+ const [meta, filePath] = line.split(" ");
13775
+ const [mode, , sha] = meta.split(/\s+/);
13776
+ return `${mode} ${sha} ${filePath}`;
13777
+ }).join("\n");
13778
+ if (indexInfo) {
13779
+ gitBuffer(repoRoot, ["update-index", "--index-info"], {
13780
+ input: indexInfo,
13781
+ env
13782
+ });
13757
13783
  }
13758
13784
  return gitWithEnv(repoRoot, ["write-tree"], env);
13759
13785
  } finally {
@@ -13789,11 +13815,22 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
13789
13815
  }
13790
13816
  function createTreeDiffPatchGz(repoRoot, fromTreeSha, toTreeSha) {
13791
13817
  if (fromTreeSha === toTreeSha) return null;
13818
+ const filteredFromTreeSha = filterOversizedFilesFromTree(
13819
+ repoRoot,
13820
+ fromTreeSha,
13821
+ {
13822
+ log: false
13823
+ }
13824
+ );
13825
+ const filteredToTreeSha = filterOversizedFilesFromTree(repoRoot, toTreeSha, {
13826
+ log: false
13827
+ });
13828
+ if (filteredFromTreeSha === filteredToTreeSha) return null;
13792
13829
  const diff = gitBuffer(repoRoot, [
13793
13830
  "diff",
13794
13831
  "--binary",
13795
- fromTreeSha,
13796
- toTreeSha
13832
+ filteredFromTreeSha,
13833
+ filteredToTreeSha
13797
13834
  ]);
13798
13835
  if (diff.length === 0) return null;
13799
13836
  return Buffer.from(gzipSync(diff));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hillclimb",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Extract and export AI coding tool logs grouped by repo",
5
5
  "license": "MIT",
6
6
  "type": "module",