hillclimb 0.8.2 → 0.8.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/dist/main.js +79 -23
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -121,7 +121,8 @@ function detectRepoRoot() {
|
|
|
121
121
|
const root = execSync("git rev-parse --show-toplevel", {
|
|
122
122
|
encoding: "utf-8",
|
|
123
123
|
timeout: 3e3,
|
|
124
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
124
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
125
|
+
windowsHide: true
|
|
125
126
|
}).trim();
|
|
126
127
|
if (!root) return null;
|
|
127
128
|
return { root, name: path2.basename(root) };
|
|
@@ -984,7 +985,7 @@ function copilotUninstallMatching(settings, eventName, predicate) {
|
|
|
984
985
|
}
|
|
985
986
|
return true;
|
|
986
987
|
}
|
|
987
|
-
var OPENCODE_PLUGIN_VERSION =
|
|
988
|
+
var OPENCODE_PLUGIN_VERSION = 8;
|
|
988
989
|
var OPENCODE_PLUGIN_MARKER = `// HILLCLIMB_OPENCODE_PLUGIN_VERSION=${OPENCODE_PLUGIN_VERSION}`;
|
|
989
990
|
var OPENCODE_PLUGIN_CONTENT = `${OPENCODE_PLUGIN_MARKER}
|
|
990
991
|
// Auto-installed by \`npx hillclimb\`. Do not edit manually \u2014 re-running
|
|
@@ -1069,6 +1070,7 @@ function spawnHillclimb(subcommand, payload) {
|
|
|
1069
1070
|
const child = spawn("npx", ["hillclimb@latest"].concat(args), {
|
|
1070
1071
|
detached: true,
|
|
1071
1072
|
stdio: ["pipe", "ignore", "ignore"],
|
|
1073
|
+
windowsHide: true,
|
|
1072
1074
|
});
|
|
1073
1075
|
child.on("error", () => {});
|
|
1074
1076
|
if (child.stdin) {
|
|
@@ -1413,7 +1415,8 @@ function trackedHookFiles(repoRoot, files) {
|
|
|
1413
1415
|
try {
|
|
1414
1416
|
const output = execFileSync("git", ["ls-files", "--", ...rels], {
|
|
1415
1417
|
cwd: repoRoot,
|
|
1416
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
1418
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1419
|
+
windowsHide: true
|
|
1417
1420
|
}).toString("utf-8").trim();
|
|
1418
1421
|
if (!output) return [];
|
|
1419
1422
|
return output.split(/\r?\n/).map((rel) => path6.join(repoRoot, rel));
|
|
@@ -1617,7 +1620,11 @@ function sleep(ms) {
|
|
|
1617
1620
|
function openBrowser(url) {
|
|
1618
1621
|
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
1619
1622
|
try {
|
|
1620
|
-
const child = spawn(cmd, args, {
|
|
1623
|
+
const child = spawn(cmd, args, {
|
|
1624
|
+
detached: true,
|
|
1625
|
+
stdio: "ignore",
|
|
1626
|
+
windowsHide: true
|
|
1627
|
+
});
|
|
1621
1628
|
child.on("error", () => {
|
|
1622
1629
|
});
|
|
1623
1630
|
child.unref();
|
|
@@ -2342,7 +2349,11 @@ var VOLATILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2342
2349
|
"wall_time_ms"
|
|
2343
2350
|
]);
|
|
2344
2351
|
function sessionsDir() {
|
|
2345
|
-
|
|
2352
|
+
if (process.env.HILLCLIMB_CODEX_SESSIONS_DIR) {
|
|
2353
|
+
return process.env.HILLCLIMB_CODEX_SESSIONS_DIR;
|
|
2354
|
+
}
|
|
2355
|
+
const codexHome = process.env.CODEX_HOME || path9.join(os3.homedir(), ".codex");
|
|
2356
|
+
return path9.join(codexHome, "sessions");
|
|
2346
2357
|
}
|
|
2347
2358
|
function asRecord(value) {
|
|
2348
2359
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -2512,6 +2523,10 @@ async function findCodexRolloutPath(sessionId) {
|
|
|
2512
2523
|
}
|
|
2513
2524
|
return void 0;
|
|
2514
2525
|
}
|
|
2526
|
+
async function isCodexInternalThread(payload, sessionId) {
|
|
2527
|
+
if (!sessionId || payload.transcript_path) return false;
|
|
2528
|
+
return await findCodexRolloutPath(sessionId) === void 0;
|
|
2529
|
+
}
|
|
2515
2530
|
function commonPrefixLength(a, b) {
|
|
2516
2531
|
const limit = Math.min(a.length, b.length);
|
|
2517
2532
|
let count = 0;
|
|
@@ -11755,14 +11770,20 @@ function getSourceBaseDir(sourceName) {
|
|
|
11755
11770
|
}
|
|
11756
11771
|
}
|
|
11757
11772
|
function archivePathFor(file) {
|
|
11773
|
+
return path11.posix.join(file.sourceName, path11.basename(file.absolutePath));
|
|
11774
|
+
}
|
|
11775
|
+
function legacyArchivePathFor(file) {
|
|
11758
11776
|
const baseDir = getSourceBaseDir(file.sourceName);
|
|
11759
11777
|
const relativePath = file.absolutePath.startsWith(baseDir) ? path11.relative(baseDir, file.absolutePath) : path11.basename(file.absolutePath);
|
|
11760
11778
|
return path11.join(file.sourceName, relativePath);
|
|
11761
11779
|
}
|
|
11762
11780
|
function addGroupToArchive(archive, group, selectedSources) {
|
|
11781
|
+
const used = /* @__PURE__ */ new Set();
|
|
11763
11782
|
for (const file of group.files) {
|
|
11764
11783
|
if (!selectedSources.has(file.sourceName)) continue;
|
|
11765
|
-
|
|
11784
|
+
let archivePath = archivePathFor(file);
|
|
11785
|
+
if (used.has(archivePath)) archivePath = legacyArchivePathFor(file);
|
|
11786
|
+
used.add(archivePath);
|
|
11766
11787
|
if (file.content) {
|
|
11767
11788
|
archive.append(file.content, { name: archivePath });
|
|
11768
11789
|
} else {
|
|
@@ -13058,7 +13079,7 @@ async function runUploadInner(payload) {
|
|
|
13058
13079
|
"warn",
|
|
13059
13080
|
`Skipping upload: missing required hook fields (session_id=${!!sessionId}, transcript_path=${!!payload.transcript_path}, cwd=${!!cwd})`
|
|
13060
13081
|
);
|
|
13061
|
-
return
|
|
13082
|
+
return "skipped";
|
|
13062
13083
|
}
|
|
13063
13084
|
const match = await findProjectForCwd(cwd);
|
|
13064
13085
|
if (!match) {
|
|
@@ -13066,13 +13087,13 @@ async function runUploadInner(payload) {
|
|
|
13066
13087
|
"warn",
|
|
13067
13088
|
`Skipping session ${sessionId}: no hillclimb config for cwd ${cwd}. Run \`npx hillclimb\` in the repo.`
|
|
13068
13089
|
);
|
|
13069
|
-
return
|
|
13090
|
+
return "skipped";
|
|
13070
13091
|
}
|
|
13071
13092
|
const { repoRoot, config } = match;
|
|
13072
13093
|
const sourceTool = await resolveSourceTool(payload, repoRoot);
|
|
13073
13094
|
if (!sourceTool) {
|
|
13074
13095
|
payload.tool = "unknown";
|
|
13075
|
-
return
|
|
13096
|
+
return "skipped";
|
|
13076
13097
|
}
|
|
13077
13098
|
payload.tool = sourceTool;
|
|
13078
13099
|
appendLog(
|
|
@@ -13086,11 +13107,18 @@ async function runUploadInner(payload) {
|
|
|
13086
13107
|
sessionId
|
|
13087
13108
|
);
|
|
13088
13109
|
if (!transcriptPath) {
|
|
13110
|
+
if (sourceTool === "codex") {
|
|
13111
|
+
appendLog(
|
|
13112
|
+
"info",
|
|
13113
|
+
`Skipping session ${sessionId}: Codex internal thread (no transcript_path and no rollout under ~/.codex/sessions); not a user session`
|
|
13114
|
+
);
|
|
13115
|
+
return "internal-thread";
|
|
13116
|
+
}
|
|
13089
13117
|
appendLog(
|
|
13090
13118
|
"warn",
|
|
13091
13119
|
`Skipping upload: missing required hook fields (session_id=true, transcript_path=false, cwd=true)`
|
|
13092
13120
|
);
|
|
13093
|
-
return
|
|
13121
|
+
return "skipped";
|
|
13094
13122
|
}
|
|
13095
13123
|
const transcriptResolved = path15.resolve(transcriptPath);
|
|
13096
13124
|
try {
|
|
@@ -13100,23 +13128,32 @@ async function runUploadInner(payload) {
|
|
|
13100
13128
|
"warn",
|
|
13101
13129
|
`Skipping session ${sessionId}: transcript_path is not a file: ${transcriptResolved}`
|
|
13102
13130
|
);
|
|
13103
|
-
return
|
|
13131
|
+
return "skipped";
|
|
13104
13132
|
}
|
|
13105
13133
|
} catch (err) {
|
|
13134
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
13135
|
+
const missing = err?.code === "ENOENT";
|
|
13136
|
+
if (missing && eventKind2 === "sessionEnd") {
|
|
13137
|
+
appendLog(
|
|
13138
|
+
"info",
|
|
13139
|
+
`Skipping session ${sessionId}: session ended without any turns; transcript was never written (${transcriptResolved})`
|
|
13140
|
+
);
|
|
13141
|
+
return "skipped";
|
|
13142
|
+
}
|
|
13106
13143
|
appendLog(
|
|
13107
13144
|
"warn",
|
|
13108
|
-
`Skipping session ${sessionId}: transcript_path not readable (${transcriptResolved}): ${
|
|
13145
|
+
`Skipping session ${sessionId}: transcript_path not readable (${transcriptResolved}): ${detail}`
|
|
13109
13146
|
);
|
|
13110
|
-
return
|
|
13147
|
+
return "skipped";
|
|
13111
13148
|
}
|
|
13112
13149
|
if (!await hasAssistantMessage(transcriptResolved)) {
|
|
13113
13150
|
appendLog(
|
|
13114
13151
|
"info",
|
|
13115
13152
|
`Skipping session ${sessionId}: transcript contains no assistant messages (nothing to upload).`
|
|
13116
13153
|
);
|
|
13117
|
-
return
|
|
13154
|
+
return "skipped";
|
|
13118
13155
|
}
|
|
13119
|
-
|
|
13156
|
+
const uploaded = await uploadSession({
|
|
13120
13157
|
sessionId,
|
|
13121
13158
|
transcriptPath: transcriptResolved,
|
|
13122
13159
|
repoRoot,
|
|
@@ -13125,6 +13162,7 @@ async function runUploadInner(payload) {
|
|
|
13125
13162
|
eventKind: eventKind2,
|
|
13126
13163
|
recordedAt
|
|
13127
13164
|
});
|
|
13165
|
+
return uploaded ? "uploaded" : "skipped";
|
|
13128
13166
|
}
|
|
13129
13167
|
var AGENT_MAX_UPLOAD_BYTES = 1024 * 1024 * 1024;
|
|
13130
13168
|
var CLI_VERSION = "0.8.0";
|
|
@@ -13344,6 +13382,10 @@ async function uploadSession(args) {
|
|
|
13344
13382
|
sourceName: sourceTool,
|
|
13345
13383
|
absolutePath: transcriptPath
|
|
13346
13384
|
});
|
|
13385
|
+
const legacyTranscriptArchivePath = legacyArchivePathFor({
|
|
13386
|
+
sourceName: sourceTool,
|
|
13387
|
+
absolutePath: transcriptPath
|
|
13388
|
+
});
|
|
13347
13389
|
const alreadySubmitted = prior?.submitted ?? false;
|
|
13348
13390
|
const submitThisUpload = config.autoSubmit && !alreadySubmitted;
|
|
13349
13391
|
if (mode === "meta-refresh" && prior?.contributionId && prior.epochMeta && cursor) {
|
|
@@ -13421,7 +13463,7 @@ async function uploadSession(args) {
|
|
|
13421
13463
|
epochMeta2 = {
|
|
13422
13464
|
transitionKind: "legacy-adopted",
|
|
13423
13465
|
baseline: "legacy-latest-zip",
|
|
13424
|
-
transcriptArchivePath,
|
|
13466
|
+
transcriptArchivePath: legacyTranscriptArchivePath,
|
|
13425
13467
|
rawByteOffset: baseCursor.rawByteOffset,
|
|
13426
13468
|
rawPrefixSha256: baseCursor.rawPrefixSha256
|
|
13427
13469
|
};
|
|
@@ -13437,7 +13479,7 @@ async function uploadSession(args) {
|
|
|
13437
13479
|
epoch: epoch2,
|
|
13438
13480
|
transitionKind: "legacy-adopted",
|
|
13439
13481
|
baseline: "legacy-latest-zip",
|
|
13440
|
-
transcriptArchivePath,
|
|
13482
|
+
transcriptArchivePath: legacyTranscriptArchivePath,
|
|
13441
13483
|
cursor: baseCursor,
|
|
13442
13484
|
recordedAt,
|
|
13443
13485
|
lineage: codexLineage
|
|
@@ -13828,6 +13870,7 @@ async function runUpload() {
|
|
|
13828
13870
|
const child = spawn2(process.execPath, [entrypoint, "upload"], {
|
|
13829
13871
|
detached: true,
|
|
13830
13872
|
stdio: ["pipe", "ignore", "ignore"],
|
|
13873
|
+
windowsHide: true,
|
|
13831
13874
|
env: workerEnv
|
|
13832
13875
|
});
|
|
13833
13876
|
child.on("error", (err) => {
|
|
@@ -13877,16 +13920,16 @@ async function runUploadWorker() {
|
|
|
13877
13920
|
const toolOverride = process.env[TOOL_ENV_FLAG];
|
|
13878
13921
|
if (toolOverride) payload.tool = toolOverride;
|
|
13879
13922
|
appendLog("info", `worker: payload summary: ${summarizePayload(payload)}`);
|
|
13880
|
-
let
|
|
13923
|
+
let outcome = "skipped";
|
|
13881
13924
|
try {
|
|
13882
|
-
|
|
13925
|
+
outcome = await runUploadInner(payload);
|
|
13883
13926
|
} catch (err) {
|
|
13884
13927
|
appendLog(
|
|
13885
13928
|
"error",
|
|
13886
13929
|
`worker: unexpected error: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
|
|
13887
13930
|
);
|
|
13888
13931
|
} finally {
|
|
13889
|
-
if (
|
|
13932
|
+
if (outcome !== "internal-thread") {
|
|
13890
13933
|
await recordDebugLogCompletion({
|
|
13891
13934
|
kind: "agent",
|
|
13892
13935
|
tool: fallbackSourceTool(payload),
|
|
@@ -13920,7 +13963,10 @@ var EXEC_OPTS = {
|
|
|
13920
13963
|
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
13921
13964
|
// A rewrite diff for a text file capped at ≤100 MB must fit. This transient
|
|
13922
13965
|
// allocation only grows as large as the actual command output.
|
|
13923
|
-
maxBuffer: 256 * 1024 * 1024
|
|
13966
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
13967
|
+
// The detached worker has no console on Windows, so without this every git
|
|
13968
|
+
// child would allocate a fresh visible console window (one flash per call).
|
|
13969
|
+
windowsHide: true
|
|
13924
13970
|
};
|
|
13925
13971
|
var MAX_ERROR_OUTPUT_CHARS = 2e3;
|
|
13926
13972
|
var DEFAULT_SNAPSHOT_LIMITS = {
|
|
@@ -14062,7 +14108,8 @@ function readTreeBlobHead(repoRoot, sha) {
|
|
|
14062
14108
|
const { stdout } = spawnSync("git", ["cat-file", "blob", sha], {
|
|
14063
14109
|
cwd: repoRoot,
|
|
14064
14110
|
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
14065
|
-
maxBuffer: BINARY_SNIFF_BYTES
|
|
14111
|
+
maxBuffer: BINARY_SNIFF_BYTES,
|
|
14112
|
+
windowsHide: true
|
|
14066
14113
|
});
|
|
14067
14114
|
return stdout?.length ? stdout.subarray(0, BINARY_SNIFF_BYTES) : null;
|
|
14068
14115
|
} catch {
|
|
@@ -14998,7 +15045,8 @@ function execGit(cwd, args) {
|
|
|
14998
15045
|
cwd,
|
|
14999
15046
|
stdio: ["pipe", "pipe", "pipe"],
|
|
15000
15047
|
timeout: 3e4,
|
|
15001
|
-
maxBuffer: 50 * 1024 * 1024
|
|
15048
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
15049
|
+
windowsHide: true
|
|
15002
15050
|
}).toString("utf-8").trim();
|
|
15003
15051
|
}
|
|
15004
15052
|
function canUploadFile(filename, buffer) {
|
|
@@ -16611,6 +16659,7 @@ async function runGitTraces() {
|
|
|
16611
16659
|
{
|
|
16612
16660
|
detached: true,
|
|
16613
16661
|
stdio: ["pipe", "ignore", "ignore"],
|
|
16662
|
+
windowsHide: true,
|
|
16614
16663
|
env: workerEnv
|
|
16615
16664
|
}
|
|
16616
16665
|
);
|
|
@@ -16679,6 +16728,13 @@ async function runGitTracesWorker() {
|
|
|
16679
16728
|
);
|
|
16680
16729
|
return;
|
|
16681
16730
|
}
|
|
16731
|
+
if (tool === "codex" && await isCodexInternalThread(payload, resolveHookSessionId(payload))) {
|
|
16732
|
+
appendLog(
|
|
16733
|
+
"info",
|
|
16734
|
+
`git-traces: skipping Codex internal thread ${resolveHookSessionId(payload)} on ${event} (no transcript_path and no rollout on disk; not a user session)`
|
|
16735
|
+
);
|
|
16736
|
+
return;
|
|
16737
|
+
}
|
|
16682
16738
|
try {
|
|
16683
16739
|
await selfHealHook2(payload, tool);
|
|
16684
16740
|
switch (eventKind2) {
|