hillclimb 0.9.2 → 0.9.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.
|
@@ -160,7 +160,7 @@ async function repoIdentity(repoRoot) {
|
|
|
160
160
|
}
|
|
161
161
|
async function logHeal(level, message) {
|
|
162
162
|
try {
|
|
163
|
-
const { appendLog: appendLog2 } = await import("./log-
|
|
163
|
+
const { appendLog: appendLog2 } = await import("./log-UZPPBFMO.js");
|
|
164
164
|
appendLog2(level, message);
|
|
165
165
|
} catch {
|
|
166
166
|
}
|
|
@@ -182,7 +182,7 @@ function matchByPath(projects, resolved) {
|
|
|
182
182
|
var healMisses = /* @__PURE__ */ new Set();
|
|
183
183
|
async function healRenamedProject(file, resolved) {
|
|
184
184
|
const dead = Object.entries(file.projects).filter(
|
|
185
|
-
([key]) =>
|
|
185
|
+
([key]) => pathIsGone(path.resolve(key))
|
|
186
186
|
);
|
|
187
187
|
if (dead.length === 0) return null;
|
|
188
188
|
const missKey = `${configPath()}|${resolved}`;
|
|
@@ -288,11 +288,26 @@ async function matchLinkedWorktree(projects, resolved) {
|
|
|
288
288
|
}
|
|
289
289
|
return null;
|
|
290
290
|
}
|
|
291
|
+
function hasNestedGitFile(root, target) {
|
|
292
|
+
let dir = target;
|
|
293
|
+
while (dir !== root && dir.startsWith(root + path.sep)) {
|
|
294
|
+
try {
|
|
295
|
+
if (fs.statSync(path.join(dir, ".git")).isFile()) return true;
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
dir = path.dirname(dir);
|
|
299
|
+
}
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
291
302
|
async function findProjectForCwd(cwd) {
|
|
292
303
|
const file = await loadProjects();
|
|
293
304
|
const resolved = path.resolve(cwd);
|
|
294
305
|
const match = matchByPath(file.projects, resolved);
|
|
295
306
|
if (match) {
|
|
307
|
+
if (hasNestedGitFile(match.repoRoot, resolved)) {
|
|
308
|
+
const nested = await matchLinkedWorktree(file.projects, resolved);
|
|
309
|
+
if (nested) return nested;
|
|
310
|
+
}
|
|
296
311
|
await backfillIdentity(file, match);
|
|
297
312
|
return { repoRoot: match.repoRoot, config: match.config };
|
|
298
313
|
}
|
|
@@ -383,5 +398,6 @@ export {
|
|
|
383
398
|
configPath,
|
|
384
399
|
loadProjects,
|
|
385
400
|
upsertProject,
|
|
401
|
+
hasNestedGitFile,
|
|
386
402
|
findProjectForCwd
|
|
387
403
|
};
|
package/dist/main.js
CHANGED
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
configDir,
|
|
6
6
|
configPath,
|
|
7
7
|
findProjectForCwd,
|
|
8
|
+
hasNestedGitFile,
|
|
8
9
|
loadProjects,
|
|
9
10
|
setLogPrefix,
|
|
10
11
|
todayLogPath,
|
|
11
12
|
upsertProject
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UPXMA7RC.js";
|
|
13
14
|
|
|
14
15
|
// src/main.ts
|
|
15
16
|
import fs27 from "fs";
|
|
@@ -992,7 +993,7 @@ var TOOLS = [
|
|
|
992
993
|
{ eventName: "Stop", command: GIT_TRACES_CMD("claude") },
|
|
993
994
|
{ eventName: "SessionEnd", command: GIT_TRACES_CMD("claude") }
|
|
994
995
|
],
|
|
995
|
-
detect: () => isDir(path4.join(
|
|
996
|
+
detect: () => isDir(path4.join(homeDir(), ".claude"))
|
|
996
997
|
},
|
|
997
998
|
{
|
|
998
999
|
tool: "cursor",
|
|
@@ -1009,7 +1010,7 @@ var TOOLS = [
|
|
|
1009
1010
|
{ eventName: "stop", command: GIT_TRACES_CMD("cursor") },
|
|
1010
1011
|
{ eventName: "sessionEnd", command: GIT_TRACES_CMD("cursor") }
|
|
1011
1012
|
],
|
|
1012
|
-
detect: () => isDir(path4.join(
|
|
1013
|
+
detect: () => isDir(path4.join(homeDir(), ".cursor"))
|
|
1013
1014
|
},
|
|
1014
1015
|
{
|
|
1015
1016
|
// opencode has no settings-file hook system (the `experimental.hook` block
|
|
@@ -1024,7 +1025,7 @@ var TOOLS = [
|
|
|
1024
1025
|
format: "opencode",
|
|
1025
1026
|
events: [],
|
|
1026
1027
|
// Events are wired inside the plugin itself; see OPENCODE_PLUGIN_CONTENT.
|
|
1027
|
-
detect: () => isDir(path4.join(
|
|
1028
|
+
detect: () => isDir(path4.join(homeDir(), ".local", "share", "opencode")) || isDir(path4.join(homeDir(), ".config", "opencode"))
|
|
1028
1029
|
},
|
|
1029
1030
|
{
|
|
1030
1031
|
// VS Code Copilot Chat. Same "no SessionEnd" constraint as Codex (the
|
|
@@ -1064,7 +1065,7 @@ var TOOLS = [
|
|
|
1064
1065
|
{ eventName: "SessionStart", command: GIT_TRACES_CMD("codex") },
|
|
1065
1066
|
{ eventName: "Stop", command: GIT_TRACES_CMD("codex") }
|
|
1066
1067
|
],
|
|
1067
|
-
detect: () => isDir(path4.join(
|
|
1068
|
+
detect: () => isDir(path4.join(homeDir(), ".codex"))
|
|
1068
1069
|
}
|
|
1069
1070
|
];
|
|
1070
1071
|
function isDir(p7) {
|
|
@@ -1074,8 +1075,11 @@ function isDir(p7) {
|
|
|
1074
1075
|
return false;
|
|
1075
1076
|
}
|
|
1076
1077
|
}
|
|
1078
|
+
function homeDir() {
|
|
1079
|
+
return process.env.HOME || os.homedir();
|
|
1080
|
+
}
|
|
1077
1081
|
function copilotChatDetect() {
|
|
1078
|
-
const home =
|
|
1082
|
+
const home = homeDir();
|
|
1079
1083
|
const suffix = path4.join("User", "globalStorage", "github.copilot-chat");
|
|
1080
1084
|
const candidates = [
|
|
1081
1085
|
path4.join(home, "Library", "Application Support", "Code", suffix),
|
|
@@ -2720,22 +2724,76 @@ import { createReadStream } from "fs";
|
|
|
2720
2724
|
import fs11 from "fs/promises";
|
|
2721
2725
|
import path10 from "path";
|
|
2722
2726
|
|
|
2727
|
+
// src/timing-profile.ts
|
|
2728
|
+
var DEFAULT_STAGE_MIN_MS = 250;
|
|
2729
|
+
var DEFAULT_LOCK_WAIT_MIN_MS = 1e3;
|
|
2730
|
+
var STAGE_MIN_MS_ENV = "HILLCLIMB_TIMING_STAGE_MIN_MS";
|
|
2731
|
+
var LOCK_WAIT_MIN_MS_ENV = "HILLCLIMB_TIMING_LOCK_WAIT_MIN_MS";
|
|
2732
|
+
function thresholdMs(name, fallback) {
|
|
2733
|
+
const raw = process.env[name];
|
|
2734
|
+
if (raw === void 0 || raw.trim() === "") return fallback;
|
|
2735
|
+
const value = Number(raw);
|
|
2736
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
2737
|
+
}
|
|
2738
|
+
function shouldLogStage(stage, elapsedMs, status) {
|
|
2739
|
+
if (stage === "lock-wait") {
|
|
2740
|
+
return elapsedMs >= thresholdMs(LOCK_WAIT_MIN_MS_ENV, DEFAULT_LOCK_WAIT_MIN_MS);
|
|
2741
|
+
}
|
|
2742
|
+
return status === "failed" || elapsedMs >= thresholdMs(STAGE_MIN_MS_ENV, DEFAULT_STAGE_MIN_MS);
|
|
2743
|
+
}
|
|
2744
|
+
var StageProfile = class {
|
|
2745
|
+
constructor(prefix) {
|
|
2746
|
+
this.prefix = prefix;
|
|
2747
|
+
}
|
|
2748
|
+
totals = /* @__PURE__ */ new Map();
|
|
2749
|
+
record(stage, elapsedMs, status) {
|
|
2750
|
+
const totals = this.totals.get(stage) ?? {
|
|
2751
|
+
count: 0,
|
|
2752
|
+
totalMs: 0,
|
|
2753
|
+
maxMs: 0,
|
|
2754
|
+
failed: 0
|
|
2755
|
+
};
|
|
2756
|
+
totals.count++;
|
|
2757
|
+
totals.totalMs += elapsedMs;
|
|
2758
|
+
totals.maxMs = Math.max(totals.maxMs, elapsedMs);
|
|
2759
|
+
if (status === "failed") totals.failed++;
|
|
2760
|
+
this.totals.set(stage, totals);
|
|
2761
|
+
}
|
|
2762
|
+
flush() {
|
|
2763
|
+
if (this.totals.size === 0) return;
|
|
2764
|
+
const entries = [...this.totals].map(([stage, totals]) => {
|
|
2765
|
+
let entry = `${stage}=${totals.totalMs}ms/${totals.count}`;
|
|
2766
|
+
if (totals.count > 1) entry += ` max=${totals.maxMs}ms`;
|
|
2767
|
+
if (totals.failed > 0) entry += ` failed=${totals.failed}`;
|
|
2768
|
+
return entry;
|
|
2769
|
+
});
|
|
2770
|
+
appendLog(
|
|
2771
|
+
"info",
|
|
2772
|
+
`${this.prefix} (stages=${this.totals.size}, ${entries.join(", ")})`
|
|
2773
|
+
);
|
|
2774
|
+
this.totals.clear();
|
|
2775
|
+
}
|
|
2776
|
+
};
|
|
2777
|
+
|
|
2723
2778
|
// src/agent-timing.ts
|
|
2779
|
+
var profile = new StageProfile("agent-upload: timing profile");
|
|
2724
2780
|
function startAgentStage(stage, context = "") {
|
|
2725
2781
|
const startedAt = Date.now();
|
|
2726
2782
|
const started = performance.now();
|
|
2727
2783
|
const suffix = context ? `, ${context}` : "";
|
|
2728
|
-
appendLog(
|
|
2729
|
-
"info",
|
|
2730
|
-
`agent-upload: timing (stage=${stage}, status=started, startedAt=${startedAt}${suffix})`
|
|
2731
|
-
);
|
|
2732
2784
|
return (status, details) => {
|
|
2785
|
+
const elapsedMs = Math.round(performance.now() - started);
|
|
2786
|
+
profile.record(stage, elapsedMs, status);
|
|
2787
|
+
if (!shouldLogStage(stage, elapsedMs, status)) return;
|
|
2733
2788
|
appendLog(
|
|
2734
2789
|
"info",
|
|
2735
|
-
`agent-upload: timing (stage=${stage}, status=${status}, elapsedMs=${
|
|
2790
|
+
`agent-upload: timing (stage=${stage}, status=${status}, elapsedMs=${elapsedMs}, startedAt=${startedAt}, finishedAt=${Date.now()}${suffix}${details ? `, ${details}` : ""})`
|
|
2736
2791
|
);
|
|
2737
2792
|
};
|
|
2738
2793
|
}
|
|
2794
|
+
function flushAgentProfile() {
|
|
2795
|
+
profile.flush();
|
|
2796
|
+
}
|
|
2739
2797
|
async function measureAgentStage(stage, action, context) {
|
|
2740
2798
|
const finish = startAgentStage(stage, context);
|
|
2741
2799
|
let status = "failed";
|
|
@@ -3199,6 +3257,47 @@ async function detectCodexLineage(childPath) {
|
|
|
3199
3257
|
}
|
|
3200
3258
|
}
|
|
3201
3259
|
|
|
3260
|
+
// src/git-traces/timing.ts
|
|
3261
|
+
import { performance as performance2 } from "perf_hooks";
|
|
3262
|
+
var profile2 = new StageProfile("git-traces: capture profile");
|
|
3263
|
+
function startCaptureStage(stage, context, startedAt = Date.now()) {
|
|
3264
|
+
const started = performance2.now();
|
|
3265
|
+
return (status, details) => {
|
|
3266
|
+
const elapsedMs = Math.round(performance2.now() - started);
|
|
3267
|
+
profile2.record(stage, elapsedMs, status);
|
|
3268
|
+
if (!shouldLogStage(stage, elapsedMs, status)) return;
|
|
3269
|
+
appendLog(
|
|
3270
|
+
"info",
|
|
3271
|
+
`git-traces: capture timing (${context}, stage=${stage}, status=${status}, elapsedMs=${elapsedMs}, startedAt=${startedAt}, finishedAt=${Date.now()}${details ? `, ${details}` : ""})`
|
|
3272
|
+
);
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
function flushCaptureProfile() {
|
|
3276
|
+
profile2.flush();
|
|
3277
|
+
}
|
|
3278
|
+
async function timeAsyncCaptureStage(stage, context, run) {
|
|
3279
|
+
const finish = startCaptureStage(stage, context);
|
|
3280
|
+
let status = "failed";
|
|
3281
|
+
try {
|
|
3282
|
+
const result = await run();
|
|
3283
|
+
status = "ok";
|
|
3284
|
+
return result;
|
|
3285
|
+
} finally {
|
|
3286
|
+
finish(status);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
function timeCaptureStage(repoRoot, stage, run) {
|
|
3290
|
+
const finish = startCaptureStage(stage, `repo=${repoRoot}`);
|
|
3291
|
+
let status = "failed";
|
|
3292
|
+
try {
|
|
3293
|
+
const result = run();
|
|
3294
|
+
status = "ok";
|
|
3295
|
+
return result;
|
|
3296
|
+
} finally {
|
|
3297
|
+
finish(status);
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3202
3301
|
// src/hook-timing.ts
|
|
3203
3302
|
var HOOK_RECEIVED_AT_ENV = "HILLCLIMB_HOOK_RECEIVED_AT";
|
|
3204
3303
|
function pinHookReceivedAt(env, now = Date.now()) {
|
|
@@ -3222,6 +3321,8 @@ async function measureHookWork(kind, action) {
|
|
|
3222
3321
|
status = "completed";
|
|
3223
3322
|
return result;
|
|
3224
3323
|
} finally {
|
|
3324
|
+
flushCaptureProfile();
|
|
3325
|
+
flushAgentProfile();
|
|
3225
3326
|
appendLog(
|
|
3226
3327
|
"info",
|
|
3227
3328
|
`hook-work: kind=${kind} status=${status} finishedAt=${Date.now()} workMs=${Math.round(performance.now() - started)} hookAgeMs=${Math.max(0, Date.now() - receivedAt)}`
|
|
@@ -4469,17 +4570,22 @@ async function gitCommonDir(cwd) {
|
|
|
4469
4570
|
async function classifyForkCwd(cwd, parentRepoRoot) {
|
|
4470
4571
|
const resolved = await canonicalPath(cwd);
|
|
4471
4572
|
const parentRoot = await canonicalPath(parentRepoRoot);
|
|
4472
|
-
|
|
4573
|
+
const below = isAtOrBelow(resolved, parentRoot);
|
|
4574
|
+
if (below && !hasNestedGitFile(parentRoot, resolved))
|
|
4473
4575
|
return { kind: "same-tree", repoRoot: parentRoot };
|
|
4474
4576
|
const toplevel = await gitOut(resolved, ["rev-parse", "--show-toplevel"]);
|
|
4475
|
-
if (!toplevel)
|
|
4577
|
+
if (!toplevel)
|
|
4578
|
+
return below ? { kind: "same-tree", repoRoot: parentRoot } : { kind: "unrelated", repoRoot: null };
|
|
4579
|
+
const worktreeRoot = await canonicalPath(toplevel);
|
|
4580
|
+
if (worktreeRoot === parentRoot)
|
|
4581
|
+
return { kind: "same-tree", repoRoot: parentRoot };
|
|
4476
4582
|
const [forkCommon, parentCommon] = await Promise.all([
|
|
4477
4583
|
gitCommonDir(resolved),
|
|
4478
4584
|
gitCommonDir(parentRoot)
|
|
4479
4585
|
]);
|
|
4480
4586
|
if (forkCommon && parentCommon && forkCommon === parentCommon)
|
|
4481
|
-
return { kind: "worktree", repoRoot:
|
|
4482
|
-
return { kind: "unrelated", repoRoot: null };
|
|
4587
|
+
return { kind: "worktree", repoRoot: worktreeRoot };
|
|
4588
|
+
return below ? { kind: "same-tree", repoRoot: parentRoot } : { kind: "unrelated", repoRoot: null };
|
|
4483
4589
|
}
|
|
4484
4590
|
async function discoverCodexForkThreads(params) {
|
|
4485
4591
|
const nowMs = params.nowMs ?? Date.now();
|
|
@@ -14662,46 +14768,6 @@ import fs17 from "fs";
|
|
|
14662
14768
|
import os7 from "os";
|
|
14663
14769
|
import path16 from "path";
|
|
14664
14770
|
import { createGzip } from "zlib";
|
|
14665
|
-
|
|
14666
|
-
// src/git-traces/timing.ts
|
|
14667
|
-
import { performance as performance2 } from "perf_hooks";
|
|
14668
|
-
function startCaptureStage(stage, context, startedAt = Date.now()) {
|
|
14669
|
-
const started = performance2.now();
|
|
14670
|
-
appendLog(
|
|
14671
|
-
"info",
|
|
14672
|
-
`git-traces: capture timing (${context}, stage=${stage}, status=started, startedAt=${startedAt})`
|
|
14673
|
-
);
|
|
14674
|
-
return (status, details) => {
|
|
14675
|
-
appendLog(
|
|
14676
|
-
"info",
|
|
14677
|
-
`git-traces: capture timing (${context}, stage=${stage}, status=${status}, elapsedMs=${Math.round(performance2.now() - started)}, startedAt=${startedAt}, finishedAt=${Date.now()}${details ? `, ${details}` : ""})`
|
|
14678
|
-
);
|
|
14679
|
-
};
|
|
14680
|
-
}
|
|
14681
|
-
async function timeAsyncCaptureStage(stage, context, run) {
|
|
14682
|
-
const finish = startCaptureStage(stage, context);
|
|
14683
|
-
let status = "failed";
|
|
14684
|
-
try {
|
|
14685
|
-
const result = await run();
|
|
14686
|
-
status = "ok";
|
|
14687
|
-
return result;
|
|
14688
|
-
} finally {
|
|
14689
|
-
finish(status);
|
|
14690
|
-
}
|
|
14691
|
-
}
|
|
14692
|
-
function timeCaptureStage(repoRoot, stage, run) {
|
|
14693
|
-
const finish = startCaptureStage(stage, `repo=${repoRoot}`);
|
|
14694
|
-
let status = "failed";
|
|
14695
|
-
try {
|
|
14696
|
-
const result = run();
|
|
14697
|
-
status = "ok";
|
|
14698
|
-
return result;
|
|
14699
|
-
} finally {
|
|
14700
|
-
finish(status);
|
|
14701
|
-
}
|
|
14702
|
-
}
|
|
14703
|
-
|
|
14704
|
-
// src/git-traces/git-ops.ts
|
|
14705
14771
|
var DEFAULT_GIT_COMMAND_TIMEOUT_MS = 12e4;
|
|
14706
14772
|
var DEFAULT_BUNDLE_TIMEOUT_MS = 3e5;
|
|
14707
14773
|
var FULL_TREE_SCAN_TIMEOUT_MS = 3e5;
|
|
@@ -15042,9 +15108,11 @@ function isGitRepo(dir) {
|
|
|
15042
15108
|
function stashRetryReason(failure) {
|
|
15043
15109
|
const status = failure.gitExitStatus;
|
|
15044
15110
|
if (status !== 1 && status !== 128) return null;
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
|
|
15111
|
+
const stderr = failure.gitStderr ?? "";
|
|
15112
|
+
if (/Unable to create [^\n]*index\.lock[^\n]*: File exists/.test(stderr)) {
|
|
15113
|
+
return "index-lock-contention";
|
|
15114
|
+
}
|
|
15115
|
+
if (status === 1 && stderr.trim() === "error: could not write index") {
|
|
15048
15116
|
return "index-lock-contention";
|
|
15049
15117
|
}
|
|
15050
15118
|
if (status === 1 && !failure.gitStderr && !failure.gitStdout) {
|
|
@@ -15052,7 +15120,60 @@ function stashRetryReason(failure) {
|
|
|
15052
15120
|
}
|
|
15053
15121
|
return null;
|
|
15054
15122
|
}
|
|
15123
|
+
var PRIVATE_INDEX_ENV = "HILLCLIMB_GIT_PRIVATE_INDEX";
|
|
15124
|
+
function withPrivateIndex(repoRoot, run) {
|
|
15125
|
+
if (process.env[PRIVATE_INDEX_ENV] === "0") return run(null);
|
|
15126
|
+
let tmpDir = null;
|
|
15127
|
+
let copy = null;
|
|
15128
|
+
try {
|
|
15129
|
+
const indexPath = path16.resolve(
|
|
15130
|
+
repoRoot,
|
|
15131
|
+
git(repoRoot, ["rev-parse", "--git-path", "index"])
|
|
15132
|
+
);
|
|
15133
|
+
tmpDir = fs17.mkdtempSync(path16.join(os7.tmpdir(), "hillclimb-index-"));
|
|
15134
|
+
copy = path16.join(tmpDir, "index");
|
|
15135
|
+
const fd = fs17.openSync(indexPath, "r");
|
|
15136
|
+
let stat;
|
|
15137
|
+
let data;
|
|
15138
|
+
try {
|
|
15139
|
+
stat = fs17.fstatSync(fd);
|
|
15140
|
+
data = fs17.readFileSync(fd);
|
|
15141
|
+
} finally {
|
|
15142
|
+
fs17.closeSync(fd);
|
|
15143
|
+
}
|
|
15144
|
+
fs17.writeFileSync(copy, data);
|
|
15145
|
+
fs17.utimesSync(copy, stat.atime, stat.mtime);
|
|
15146
|
+
} catch (err) {
|
|
15147
|
+
const missing = err.code === "ENOENT";
|
|
15148
|
+
appendLog(
|
|
15149
|
+
missing ? "info" : "warn",
|
|
15150
|
+
`git-traces: private index unavailable; capturing on the repository index (repo=${repoRoot}, reason=${err instanceof Error ? err.message : String(err)})`
|
|
15151
|
+
);
|
|
15152
|
+
copy = null;
|
|
15153
|
+
}
|
|
15154
|
+
try {
|
|
15155
|
+
return run(copy ? privateIndexEnv(copy) : null);
|
|
15156
|
+
} finally {
|
|
15157
|
+
if (tmpDir) fs17.rmSync(tmpDir, { recursive: true, force: true });
|
|
15158
|
+
}
|
|
15159
|
+
}
|
|
15160
|
+
function privateIndexEnv(indexPath) {
|
|
15161
|
+
const existing = Number(process.env.GIT_CONFIG_COUNT);
|
|
15162
|
+
const slot = Number.isInteger(existing) && existing > 0 ? existing : 0;
|
|
15163
|
+
return {
|
|
15164
|
+
GIT_INDEX_FILE: indexPath,
|
|
15165
|
+
GIT_CONFIG_COUNT: String(slot + 1),
|
|
15166
|
+
[`GIT_CONFIG_KEY_${slot}`]: "core.splitIndex",
|
|
15167
|
+
[`GIT_CONFIG_VALUE_${slot}`]: "false"
|
|
15168
|
+
};
|
|
15169
|
+
}
|
|
15055
15170
|
function captureWorkingCommitSha(repoRoot) {
|
|
15171
|
+
return withPrivateIndex(
|
|
15172
|
+
repoRoot,
|
|
15173
|
+
(indexEnv) => captureWorkingCommitShaOnIndex(repoRoot, indexEnv)
|
|
15174
|
+
);
|
|
15175
|
+
}
|
|
15176
|
+
function captureWorkingCommitShaOnIndex(repoRoot, indexEnv) {
|
|
15056
15177
|
for (let attempt = 1; ; attempt++) {
|
|
15057
15178
|
let sha;
|
|
15058
15179
|
try {
|
|
@@ -15063,6 +15184,7 @@ function captureWorkingCommitSha(repoRoot) {
|
|
|
15063
15184
|
// Keep the two expected stderr classifications independent of locale.
|
|
15064
15185
|
gitWithEnv(repoRoot, ["stash", "create"], {
|
|
15065
15186
|
...collectorIdentityEnv(),
|
|
15187
|
+
...indexEnv,
|
|
15066
15188
|
LC_ALL: "C"
|
|
15067
15189
|
})
|
|
15068
15190
|
)
|
|
@@ -15075,7 +15197,10 @@ function captureWorkingCommitSha(repoRoot) {
|
|
|
15075
15197
|
"info",
|
|
15076
15198
|
`git-traces: stash create unavailable (repo=${repoRoot}, reason=unborn-head); capturing index tree`
|
|
15077
15199
|
);
|
|
15078
|
-
const tree =
|
|
15200
|
+
const tree = gitWithEnv(repoRoot, ["write-tree"], {
|
|
15201
|
+
...process.env,
|
|
15202
|
+
...indexEnv
|
|
15203
|
+
});
|
|
15079
15204
|
return commitTree(repoRoot, tree, "empty baseline");
|
|
15080
15205
|
}
|
|
15081
15206
|
const retryReason = stashRetryReason(failure);
|
|
@@ -15777,7 +15902,10 @@ function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersio
|
|
|
15777
15902
|
const headSha = safeGit(repoRoot, ["rev-parse", "HEAD"]) ?? "unknown";
|
|
15778
15903
|
const branch = safeGit(repoRoot, ["rev-parse", "--abbrev-ref", "HEAD"]) ?? null;
|
|
15779
15904
|
const remoteUrl = safeGit(repoRoot, ["config", "--get", "remote.origin.url"]) ?? null;
|
|
15780
|
-
const statusRaw =
|
|
15905
|
+
const statusRaw = withPrivateIndex(
|
|
15906
|
+
repoRoot,
|
|
15907
|
+
(indexEnv) => safeGitBuffer(repoRoot, ["status", "--porcelain=v1", "-z"], indexEnv)
|
|
15908
|
+
) ?? Buffer.alloc(0);
|
|
15781
15909
|
const dirtyFiles = parseDirtyFilesFromStatus(statusRaw);
|
|
15782
15910
|
const authorName = safeGit(repoRoot, ["config", "user.name"]) ?? "";
|
|
15783
15911
|
const authorEmail = safeGit(repoRoot, ["config", "user.email"]) ?? "";
|
|
@@ -15926,9 +16054,13 @@ function safeGit(repoRoot, args) {
|
|
|
15926
16054
|
return null;
|
|
15927
16055
|
}
|
|
15928
16056
|
}
|
|
15929
|
-
function safeGitBuffer(repoRoot, args) {
|
|
16057
|
+
function safeGitBuffer(repoRoot, args, env = null) {
|
|
15930
16058
|
try {
|
|
15931
|
-
return gitBuffer(
|
|
16059
|
+
return gitBuffer(
|
|
16060
|
+
repoRoot,
|
|
16061
|
+
args,
|
|
16062
|
+
env ? { env: { ...process.env, ...env } } : {}
|
|
16063
|
+
);
|
|
15932
16064
|
} catch (err) {
|
|
15933
16065
|
if (isGitTimeoutError(err)) {
|
|
15934
16066
|
appendLog(
|
|
@@ -16837,7 +16969,7 @@ async function uploadArtifact(client, contributionId, filename, mimeType, buffer
|
|
|
16837
16969
|
}
|
|
16838
16970
|
|
|
16839
16971
|
// package.json
|
|
16840
|
-
var version = "0.9.
|
|
16972
|
+
var version = "0.9.3";
|
|
16841
16973
|
|
|
16842
16974
|
// src/version.ts
|
|
16843
16975
|
var CLI_VERSION = version;
|
|
@@ -16978,8 +17110,14 @@ function isPhantomClaudeHook(payload, toolOverride) {
|
|
|
16978
17110
|
}
|
|
16979
17111
|
var MAX_FORK_SWEEP_THREADS = 8;
|
|
16980
17112
|
async function runUploadInner(payload, options = {}) {
|
|
16981
|
-
|
|
16982
|
-
|
|
17113
|
+
let outcome;
|
|
17114
|
+
let failure;
|
|
17115
|
+
try {
|
|
17116
|
+
outcome = await runUploadForSession(payload, options);
|
|
17117
|
+
} catch (err) {
|
|
17118
|
+
failure = err;
|
|
17119
|
+
}
|
|
17120
|
+
const settled = failure !== void 0 || outcome === "uploaded" || outcome === "unchanged" || outcome === "skipped";
|
|
16983
17121
|
if (!options.sweptFrom && payload.tool === "codex" && settled) {
|
|
16984
17122
|
await sweepCodexForkTranscripts(payload);
|
|
16985
17123
|
}
|
|
@@ -16989,6 +17127,7 @@ async function runUploadInner(payload, options = {}) {
|
|
|
16989
17127
|
if (!options.sweptFrom && settled) {
|
|
16990
17128
|
await sweepStrandedAgentUploads(resolveHookSessionId(payload));
|
|
16991
17129
|
}
|
|
17130
|
+
if (failure !== void 0) throw failure;
|
|
16992
17131
|
return outcome;
|
|
16993
17132
|
}
|
|
16994
17133
|
var MAX_SUBAGENT_SWEEP = 32;
|