hillclimb 0.5.2 → 0.5.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 (3) hide show
  1. package/README.md +13 -24
  2. package/dist/cli.js +23 -19
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,43 +1,32 @@
1
1
  # hillclimb
2
2
 
3
- Extract AI coding tool sessions (Claude Code, Cursor, Codex, opencode, GitHub Copilot Chat) and upload them to a Hillclimb project.
3
+ Capture AI coding sessions and upload agent traces, ATIF traces, git traces, and
4
+ debug logs to Hillclimb.
4
5
 
5
- ## Quickstart
6
+ Supported tools: Claude Code, Cursor, Codex, opencode, and GitHub Copilot Chat.
6
7
 
7
- ```sh
8
- npx hillclimb
9
- ```
8
+ ## Quickstart
10
9
 
11
- Run in any git repo. Sessions auto-upload when they end.
10
+ Run once from a git repo:
12
11
 
13
12
  ```sh
14
- npx hillclimb status
13
+ npx hillclimb
15
14
  ```
16
15
 
17
- Shows login and hook status for the current repo.
18
-
19
- ## Hooks
20
-
21
- `hillclimb` detects which tools you use (`~/.claude`, `~/.cursor`, `~/.codex`, `~/.local/share/opencode`, VS Code GitHub Copilot Chat storage) and installs upload + git-trace hooks for each. You might want to gitignore the respective directories for the tools you use in your repo.
22
-
23
- To manually export historical logs instead of configuring auto-upload, run:
24
-
25
- ```sh
26
- npx hillclimb export
27
- ```
16
+ Hillclimb signs you in, connects the repo to a project, installs local hooks for
17
+ detected tools, and uploads future sessions automatically.
28
18
 
29
19
  ## Logs
30
20
 
31
- Hillclimb writes one log file per day to `~/.hillclimb/logs/YYYY-MM-DD.log`. Timestamps and the daily filename are in Pacific Time, so logs line up with Hillclimb's internal clock regardless of your timezone.
32
-
33
- Each hook firing tags every line it writes (parent + worker) with a short correlation ID, e.g. `[a1b2c3]`. To follow one flow across interleaved hook runs:
21
+ Daily logs are written to:
34
22
 
35
23
  ```sh
36
- grep '\[a1b2c3\]' ~/.hillclimb/logs/$(TZ=America/Los_Angeles date +%F).log
24
+ ~/.hillclimb/logs/YYYY-MM-DD.log
37
25
  ```
38
26
 
39
- HTTP calls log method, path, status, and latency. Auth codes and presigned-URL signatures are deliberately omitted.
27
+ If a hook reports `npx: command not found`, install Node/npm or make sure `npx`
28
+ is available to the agent hook shell.
40
29
 
41
30
  ## License
42
31
 
43
- MIT see [LICENSE](LICENSE).
32
+ MIT - see [LICENSE](LICENSE).
package/dist/cli.js CHANGED
@@ -14080,7 +14080,11 @@ var EXEC_OPTS = {
14080
14080
  };
14081
14081
  var MAX_ERROR_OUTPUT_CHARS = 2e3;
14082
14082
  var MAX_SNAPSHOT_FILE_BYTES = 10 * 1024 * 1024;
14083
+ var EXCLUDED_SNAPSHOT_EXTENSIONS = /* @__PURE__ */ new Set([".pdf"]);
14083
14084
  var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
14085
+ function hasExcludedSnapshotExtension(filePath) {
14086
+ return EXCLUDED_SNAPSHOT_EXTENSIONS.has(path16.extname(filePath).toLowerCase());
14087
+ }
14084
14088
  function quoteGitArg(arg) {
14085
14089
  if (/^[A-Za-z0-9_./:=@%+,-]+$/.test(arg)) return arg;
14086
14090
  return JSON.stringify(arg);
@@ -14184,10 +14188,8 @@ function recordOmittedSnapshotFile(omittedFiles, file, options = {}) {
14184
14188
  omittedFiles?.push(file);
14185
14189
  if (options.log === false) return;
14186
14190
  const action = file.tracked ? "omitting tracked" : "skipping untracked";
14187
- appendLog(
14188
- "warn",
14189
- `git-traces: ${action} ${file.path} (${file.sizeBytes} bytes > ${MAX_SNAPSHOT_FILE_BYTES} limit)`
14190
- );
14191
+ const reason = file.reason === "file-over-limit" ? `${file.sizeBytes} bytes > ${MAX_SNAPSHOT_FILE_BYTES} limit` : "excluded extension";
14192
+ appendLog("warn", `git-traces: ${action} ${file.path} (${reason})`);
14191
14193
  }
14192
14194
  function parseLsTreeLongZ(output) {
14193
14195
  const entries = [];
@@ -14205,24 +14207,25 @@ function parseLsTreeLongZ(output) {
14205
14207
  }
14206
14208
  return entries;
14207
14209
  }
14208
- function listOversizedTreeFiles(repoRoot, treeSha, options = {}) {
14210
+ function listOmittedTreeFiles(repoRoot, treeSha, options = {}) {
14209
14211
  const output = gitBuffer(repoRoot, ["ls-tree", "-r", "-l", "-z", treeSha]);
14210
- const oversized = [];
14212
+ const omitted = [];
14211
14213
  for (const entry of parseLsTreeLongZ(output)) {
14212
- if (entry.sizeBytes <= MAX_SNAPSHOT_FILE_BYTES) continue;
14214
+ const reason = hasExcludedSnapshotExtension(entry.path) ? "excluded-extension" : entry.sizeBytes > MAX_SNAPSHOT_FILE_BYTES ? "file-over-limit" : null;
14215
+ if (!reason) continue;
14213
14216
  const file = {
14214
14217
  path: entry.path,
14215
14218
  sizeBytes: entry.sizeBytes,
14216
14219
  tracked: true,
14217
- reason: "file-over-limit",
14220
+ reason,
14218
14221
  gitObjectSha: entry.sha
14219
14222
  };
14220
- oversized.push(file);
14223
+ omitted.push(file);
14221
14224
  recordOmittedSnapshotFile(options.omittedFiles, file, {
14222
14225
  log: options.log
14223
14226
  });
14224
14227
  }
14225
- return oversized;
14228
+ return omitted;
14226
14229
  }
14227
14230
  function removePathsFromIndex(repoRoot, env, paths) {
14228
14231
  if (paths.length === 0) return;
@@ -14231,9 +14234,9 @@ function removePathsFromIndex(repoRoot, env, paths) {
14231
14234
  env
14232
14235
  });
14233
14236
  }
14234
- function filterOversizedFilesFromTree(repoRoot, treeSha, options = {}) {
14235
- const oversizedFiles = listOversizedTreeFiles(repoRoot, treeSha, options);
14236
- if (oversizedFiles.length === 0) return treeSha;
14237
+ function filterOmittedFilesFromTree(repoRoot, treeSha, options = {}) {
14238
+ const omittedFiles = listOmittedTreeFiles(repoRoot, treeSha, options);
14239
+ if (omittedFiles.length === 0) return treeSha;
14237
14240
  const tmpIndex = path16.join(
14238
14241
  os7.tmpdir(),
14239
14242
  `hillclimb-filter-${Date.now()}-${process.pid}`
@@ -14244,7 +14247,7 @@ function filterOversizedFilesFromTree(repoRoot, treeSha, options = {}) {
14244
14247
  removePathsFromIndex(
14245
14248
  repoRoot,
14246
14249
  env,
14247
- oversizedFiles.map((file) => file.path)
14250
+ omittedFiles.map((file) => file.path)
14248
14251
  );
14249
14252
  return gitWithEnv(repoRoot, ["write-tree"], env);
14250
14253
  } finally {
@@ -14267,12 +14270,13 @@ function buildUntrackedTree(repoRoot, omittedFiles) {
14267
14270
  if (!relPath) continue;
14268
14271
  try {
14269
14272
  const stat = fs12.lstatSync(path16.join(repoRoot, relPath));
14270
- if (stat.size > MAX_SNAPSHOT_FILE_BYTES) {
14273
+ const reason = hasExcludedSnapshotExtension(relPath) ? "excluded-extension" : stat.size > MAX_SNAPSHOT_FILE_BYTES ? "file-over-limit" : null;
14274
+ if (reason) {
14271
14275
  recordOmittedSnapshotFile(omittedFiles, {
14272
14276
  path: relPath,
14273
14277
  sizeBytes: stat.size,
14274
14278
  tracked: false,
14275
- reason: "file-over-limit"
14279
+ reason
14276
14280
  });
14277
14281
  continue;
14278
14282
  }
@@ -14301,7 +14305,7 @@ function buildUntrackedTree(repoRoot, omittedFiles) {
14301
14305
  }
14302
14306
  function buildSnapshotTree(repoRoot, stashSha, options = {}) {
14303
14307
  const trackedTree = git(repoRoot, ["rev-parse", `${stashSha}^{tree}`]);
14304
- const filteredTrackedTree = filterOversizedFilesFromTree(
14308
+ const filteredTrackedTree = filterOmittedFilesFromTree(
14305
14309
  repoRoot,
14306
14310
  trackedTree,
14307
14311
  {
@@ -14373,14 +14377,14 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
14373
14377
  }
14374
14378
  function createTreeDiffPatchGz(repoRoot, fromTreeSha, toTreeSha) {
14375
14379
  if (fromTreeSha === toTreeSha) return null;
14376
- const filteredFromTreeSha = filterOversizedFilesFromTree(
14380
+ const filteredFromTreeSha = filterOmittedFilesFromTree(
14377
14381
  repoRoot,
14378
14382
  fromTreeSha,
14379
14383
  {
14380
14384
  log: false
14381
14385
  }
14382
14386
  );
14383
- const filteredToTreeSha = filterOversizedFilesFromTree(repoRoot, toTreeSha, {
14387
+ const filteredToTreeSha = filterOmittedFilesFromTree(repoRoot, toTreeSha, {
14384
14388
  log: false
14385
14389
  });
14386
14390
  if (filteredFromTreeSha === filteredToTreeSha) return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hillclimb",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Extract and export AI coding tool logs grouped by repo",
5
5
  "license": "MIT",
6
6
  "type": "module",