hillclimb 0.5.2 → 0.5.4

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/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
@@ -10475,6 +10475,22 @@ var patterns_default = {
10475
10475
  ]
10476
10476
  };
10477
10477
 
10478
+ // src/middleware/patterns-local.json
10479
+ var patterns_local_default = {
10480
+ patterns: [
10481
+ {
10482
+ name: "Hugging Face User Access Token",
10483
+ regex: "(?:(?<![A-Za-z0-9])|(?<=\\\\[nrt]))hf_[A-Za-z0-9]{34,40}",
10484
+ confidence: "high"
10485
+ },
10486
+ {
10487
+ name: "Hugging Face Organization API Token",
10488
+ regex: "(?:(?<![A-Za-z0-9])|(?<=\\\\[nrt]))api_org_[A-Za-z0-9]{34,40}",
10489
+ confidence: "high"
10490
+ }
10491
+ ]
10492
+ };
10493
+
10478
10494
  // src/middleware/pattern-core.ts
10479
10495
  var DELIMITER_SUFFIX = /\(=\| =\|:\| :\)$/;
10480
10496
  var HAS_VALUE_MATCHER = /[[{+*]|\\[wdWDsS]/;
@@ -10510,7 +10526,8 @@ function countCaptureGroups(source) {
10510
10526
  }
10511
10527
  function compilePatterns() {
10512
10528
  const patterns = [];
10513
- for (const p7 of patterns_default.patterns) {
10529
+ const allPatterns = [...patterns_default.patterns, ...patterns_local_default.patterns];
10530
+ for (const p7 of allPatterns) {
10514
10531
  if (p7.confidence !== "high") continue;
10515
10532
  if (DELIMITER_SUFFIX.test(p7.regex)) continue;
10516
10533
  if (!HAS_VALUE_MATCHER.test(p7.regex)) continue;
@@ -14080,7 +14097,11 @@ var EXEC_OPTS = {
14080
14097
  };
14081
14098
  var MAX_ERROR_OUTPUT_CHARS = 2e3;
14082
14099
  var MAX_SNAPSHOT_FILE_BYTES = 10 * 1024 * 1024;
14100
+ var EXCLUDED_SNAPSHOT_EXTENSIONS = /* @__PURE__ */ new Set([".pdf"]);
14083
14101
  var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
14102
+ function hasExcludedSnapshotExtension(filePath) {
14103
+ return EXCLUDED_SNAPSHOT_EXTENSIONS.has(path16.extname(filePath).toLowerCase());
14104
+ }
14084
14105
  function quoteGitArg(arg) {
14085
14106
  if (/^[A-Za-z0-9_./:=@%+,-]+$/.test(arg)) return arg;
14086
14107
  return JSON.stringify(arg);
@@ -14184,10 +14205,8 @@ function recordOmittedSnapshotFile(omittedFiles, file, options = {}) {
14184
14205
  omittedFiles?.push(file);
14185
14206
  if (options.log === false) return;
14186
14207
  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
- );
14208
+ const reason = file.reason === "file-over-limit" ? `${file.sizeBytes} bytes > ${MAX_SNAPSHOT_FILE_BYTES} limit` : "excluded extension";
14209
+ appendLog("warn", `git-traces: ${action} ${file.path} (${reason})`);
14191
14210
  }
14192
14211
  function parseLsTreeLongZ(output) {
14193
14212
  const entries = [];
@@ -14205,24 +14224,25 @@ function parseLsTreeLongZ(output) {
14205
14224
  }
14206
14225
  return entries;
14207
14226
  }
14208
- function listOversizedTreeFiles(repoRoot, treeSha, options = {}) {
14227
+ function listOmittedTreeFiles(repoRoot, treeSha, options = {}) {
14209
14228
  const output = gitBuffer(repoRoot, ["ls-tree", "-r", "-l", "-z", treeSha]);
14210
- const oversized = [];
14229
+ const omitted = [];
14211
14230
  for (const entry of parseLsTreeLongZ(output)) {
14212
- if (entry.sizeBytes <= MAX_SNAPSHOT_FILE_BYTES) continue;
14231
+ const reason = hasExcludedSnapshotExtension(entry.path) ? "excluded-extension" : entry.sizeBytes > MAX_SNAPSHOT_FILE_BYTES ? "file-over-limit" : null;
14232
+ if (!reason) continue;
14213
14233
  const file = {
14214
14234
  path: entry.path,
14215
14235
  sizeBytes: entry.sizeBytes,
14216
14236
  tracked: true,
14217
- reason: "file-over-limit",
14237
+ reason,
14218
14238
  gitObjectSha: entry.sha
14219
14239
  };
14220
- oversized.push(file);
14240
+ omitted.push(file);
14221
14241
  recordOmittedSnapshotFile(options.omittedFiles, file, {
14222
14242
  log: options.log
14223
14243
  });
14224
14244
  }
14225
- return oversized;
14245
+ return omitted;
14226
14246
  }
14227
14247
  function removePathsFromIndex(repoRoot, env, paths) {
14228
14248
  if (paths.length === 0) return;
@@ -14231,9 +14251,9 @@ function removePathsFromIndex(repoRoot, env, paths) {
14231
14251
  env
14232
14252
  });
14233
14253
  }
14234
- function filterOversizedFilesFromTree(repoRoot, treeSha, options = {}) {
14235
- const oversizedFiles = listOversizedTreeFiles(repoRoot, treeSha, options);
14236
- if (oversizedFiles.length === 0) return treeSha;
14254
+ function filterOmittedFilesFromTree(repoRoot, treeSha, options = {}) {
14255
+ const omittedFiles = listOmittedTreeFiles(repoRoot, treeSha, options);
14256
+ if (omittedFiles.length === 0) return treeSha;
14237
14257
  const tmpIndex = path16.join(
14238
14258
  os7.tmpdir(),
14239
14259
  `hillclimb-filter-${Date.now()}-${process.pid}`
@@ -14244,7 +14264,7 @@ function filterOversizedFilesFromTree(repoRoot, treeSha, options = {}) {
14244
14264
  removePathsFromIndex(
14245
14265
  repoRoot,
14246
14266
  env,
14247
- oversizedFiles.map((file) => file.path)
14267
+ omittedFiles.map((file) => file.path)
14248
14268
  );
14249
14269
  return gitWithEnv(repoRoot, ["write-tree"], env);
14250
14270
  } finally {
@@ -14267,12 +14287,13 @@ function buildUntrackedTree(repoRoot, omittedFiles) {
14267
14287
  if (!relPath) continue;
14268
14288
  try {
14269
14289
  const stat = fs12.lstatSync(path16.join(repoRoot, relPath));
14270
- if (stat.size > MAX_SNAPSHOT_FILE_BYTES) {
14290
+ const reason = hasExcludedSnapshotExtension(relPath) ? "excluded-extension" : stat.size > MAX_SNAPSHOT_FILE_BYTES ? "file-over-limit" : null;
14291
+ if (reason) {
14271
14292
  recordOmittedSnapshotFile(omittedFiles, {
14272
14293
  path: relPath,
14273
14294
  sizeBytes: stat.size,
14274
14295
  tracked: false,
14275
- reason: "file-over-limit"
14296
+ reason
14276
14297
  });
14277
14298
  continue;
14278
14299
  }
@@ -14301,7 +14322,7 @@ function buildUntrackedTree(repoRoot, omittedFiles) {
14301
14322
  }
14302
14323
  function buildSnapshotTree(repoRoot, stashSha, options = {}) {
14303
14324
  const trackedTree = git(repoRoot, ["rev-parse", `${stashSha}^{tree}`]);
14304
- const filteredTrackedTree = filterOversizedFilesFromTree(
14325
+ const filteredTrackedTree = filterOmittedFilesFromTree(
14305
14326
  repoRoot,
14306
14327
  trackedTree,
14307
14328
  {
@@ -14373,14 +14394,14 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
14373
14394
  }
14374
14395
  function createTreeDiffPatchGz(repoRoot, fromTreeSha, toTreeSha) {
14375
14396
  if (fromTreeSha === toTreeSha) return null;
14376
- const filteredFromTreeSha = filterOversizedFilesFromTree(
14397
+ const filteredFromTreeSha = filterOmittedFilesFromTree(
14377
14398
  repoRoot,
14378
14399
  fromTreeSha,
14379
14400
  {
14380
14401
  log: false
14381
14402
  }
14382
14403
  );
14383
- const filteredToTreeSha = filterOversizedFilesFromTree(repoRoot, toTreeSha, {
14404
+ const filteredToTreeSha = filterOmittedFilesFromTree(repoRoot, toTreeSha, {
14384
14405
  log: false
14385
14406
  });
14386
14407
  if (filteredFromTreeSha === filteredToTreeSha) return null;
@@ -8057,6 +8057,22 @@ var patterns_default = {
8057
8057
  ]
8058
8058
  };
8059
8059
 
8060
+ // src/middleware/patterns-local.json
8061
+ var patterns_local_default = {
8062
+ patterns: [
8063
+ {
8064
+ name: "Hugging Face User Access Token",
8065
+ regex: "(?:(?<![A-Za-z0-9])|(?<=\\\\[nrt]))hf_[A-Za-z0-9]{34,40}",
8066
+ confidence: "high"
8067
+ },
8068
+ {
8069
+ name: "Hugging Face Organization API Token",
8070
+ regex: "(?:(?<![A-Za-z0-9])|(?<=\\\\[nrt]))api_org_[A-Za-z0-9]{34,40}",
8071
+ confidence: "high"
8072
+ }
8073
+ ]
8074
+ };
8075
+
8060
8076
  // src/middleware/pattern-core.ts
8061
8077
  var DELIMITER_SUFFIX = /\(=\| =\|:\| :\)$/;
8062
8078
  var HAS_VALUE_MATCHER = /[[{+*]|\\[wdWDsS]/;
@@ -8092,7 +8108,8 @@ function countCaptureGroups(source) {
8092
8108
  }
8093
8109
  function compilePatterns() {
8094
8110
  const patterns = [];
8095
- for (const p of patterns_default.patterns) {
8111
+ const allPatterns = [...patterns_default.patterns, ...patterns_local_default.patterns];
8112
+ for (const p of allPatterns) {
8096
8113
  if (p.confidence !== "high") continue;
8097
8114
  if (DELIMITER_SUFFIX.test(p.regex)) continue;
8098
8115
  if (!HAS_VALUE_MATCHER.test(p.regex)) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hillclimb",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Extract and export AI coding tool logs grouped by repo",
5
5
  "license": "MIT",
6
6
  "type": "module",