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 +13 -24
- package/dist/cli.js +41 -20
- package/dist/pattern-worker.js +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,43 +1,32 @@
|
|
|
1
1
|
# hillclimb
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Capture AI coding sessions and upload agent traces, ATIF traces, git traces, and
|
|
4
|
+
debug logs to Hillclimb.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Supported tools: Claude Code, Cursor, Codex, opencode, and GitHub Copilot Chat.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
npx hillclimb
|
|
9
|
-
```
|
|
8
|
+
## Quickstart
|
|
10
9
|
|
|
11
|
-
Run
|
|
10
|
+
Run once from a git repo:
|
|
12
11
|
|
|
13
12
|
```sh
|
|
14
|
-
npx hillclimb
|
|
13
|
+
npx hillclimb
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24
|
+
~/.hillclimb/logs/YYYY-MM-DD.log
|
|
37
25
|
```
|
|
38
26
|
|
|
39
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
14188
|
-
|
|
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
|
|
14227
|
+
function listOmittedTreeFiles(repoRoot, treeSha, options = {}) {
|
|
14209
14228
|
const output = gitBuffer(repoRoot, ["ls-tree", "-r", "-l", "-z", treeSha]);
|
|
14210
|
-
const
|
|
14229
|
+
const omitted = [];
|
|
14211
14230
|
for (const entry of parseLsTreeLongZ(output)) {
|
|
14212
|
-
|
|
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
|
|
14237
|
+
reason,
|
|
14218
14238
|
gitObjectSha: entry.sha
|
|
14219
14239
|
};
|
|
14220
|
-
|
|
14240
|
+
omitted.push(file);
|
|
14221
14241
|
recordOmittedSnapshotFile(options.omittedFiles, file, {
|
|
14222
14242
|
log: options.log
|
|
14223
14243
|
});
|
|
14224
14244
|
}
|
|
14225
|
-
return
|
|
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
|
|
14235
|
-
const
|
|
14236
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
14397
|
+
const filteredFromTreeSha = filterOmittedFilesFromTree(
|
|
14377
14398
|
repoRoot,
|
|
14378
14399
|
fromTreeSha,
|
|
14379
14400
|
{
|
|
14380
14401
|
log: false
|
|
14381
14402
|
}
|
|
14382
14403
|
);
|
|
14383
|
-
const filteredToTreeSha =
|
|
14404
|
+
const filteredToTreeSha = filterOmittedFilesFromTree(repoRoot, toTreeSha, {
|
|
14384
14405
|
log: false
|
|
14385
14406
|
});
|
|
14386
14407
|
if (filteredFromTreeSha === filteredToTreeSha) return null;
|
package/dist/pattern-worker.js
CHANGED
|
@@ -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
|
-
|
|
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;
|