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.
- package/README.md +13 -24
- package/dist/cli.js +23 -19
- 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
|
@@ -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
|
-
|
|
14188
|
-
|
|
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
|
|
14210
|
+
function listOmittedTreeFiles(repoRoot, treeSha, options = {}) {
|
|
14209
14211
|
const output = gitBuffer(repoRoot, ["ls-tree", "-r", "-l", "-z", treeSha]);
|
|
14210
|
-
const
|
|
14212
|
+
const omitted = [];
|
|
14211
14213
|
for (const entry of parseLsTreeLongZ(output)) {
|
|
14212
|
-
|
|
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
|
|
14220
|
+
reason,
|
|
14218
14221
|
gitObjectSha: entry.sha
|
|
14219
14222
|
};
|
|
14220
|
-
|
|
14223
|
+
omitted.push(file);
|
|
14221
14224
|
recordOmittedSnapshotFile(options.omittedFiles, file, {
|
|
14222
14225
|
log: options.log
|
|
14223
14226
|
});
|
|
14224
14227
|
}
|
|
14225
|
-
return
|
|
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
|
|
14235
|
-
const
|
|
14236
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
14380
|
+
const filteredFromTreeSha = filterOmittedFilesFromTree(
|
|
14377
14381
|
repoRoot,
|
|
14378
14382
|
fromTreeSha,
|
|
14379
14383
|
{
|
|
14380
14384
|
log: false
|
|
14381
14385
|
}
|
|
14382
14386
|
);
|
|
14383
|
-
const filteredToTreeSha =
|
|
14387
|
+
const filteredToTreeSha = filterOmittedFilesFromTree(repoRoot, toTreeSha, {
|
|
14384
14388
|
log: false
|
|
14385
14389
|
});
|
|
14386
14390
|
if (filteredFromTreeSha === filteredToTreeSha) return null;
|