hillclimb 0.1.1 → 0.1.2
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/cli.js +89 -61
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11941,7 +11941,7 @@ function detectTransitionKind(repoRoot, prevHeadSha, nextHeadSha) {
|
|
|
11941
11941
|
return "branch-switch";
|
|
11942
11942
|
}
|
|
11943
11943
|
}
|
|
11944
|
-
function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersion, epoch, prevHeadSha, transitionKind) {
|
|
11944
|
+
function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersion, epoch, prevHeadSha, transitionKind, startedAt) {
|
|
11945
11945
|
const headSha = safeGit(repoRoot, ["rev-parse", "HEAD"]) ?? "unknown";
|
|
11946
11946
|
const branch = safeGit(repoRoot, ["rev-parse", "--abbrev-ref", "HEAD"]) ?? null;
|
|
11947
11947
|
const remoteUrl = safeGit(repoRoot, ["config", "--get", "remote.origin.url"]) ?? null;
|
|
@@ -11953,7 +11953,7 @@ function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersio
|
|
|
11953
11953
|
return {
|
|
11954
11954
|
sessionId,
|
|
11955
11955
|
tool,
|
|
11956
|
-
startedAt
|
|
11956
|
+
startedAt,
|
|
11957
11957
|
cwd: repoRoot,
|
|
11958
11958
|
patchFormat: "incremental",
|
|
11959
11959
|
epoch,
|
|
@@ -12111,7 +12111,7 @@ import crypto from "crypto";
|
|
|
12111
12111
|
import fs10 from "fs";
|
|
12112
12112
|
import os7 from "os";
|
|
12113
12113
|
import path13 from "path";
|
|
12114
|
-
var CURRENT_SCHEMA_VERSION =
|
|
12114
|
+
var CURRENT_SCHEMA_VERSION = 3;
|
|
12115
12115
|
var STATE_DIR = path13.join(os7.homedir(), ".hillclimb", "git-traces");
|
|
12116
12116
|
function stateFileForRepo(repoRoot, tool) {
|
|
12117
12117
|
const hash = crypto.createHash("sha256").update(`${path13.resolve(repoRoot)}\0${tool}`).digest("hex").slice(0, 16);
|
|
@@ -12237,7 +12237,16 @@ async function uploadFile(client, contributionId, filename, mimeType, buffer) {
|
|
|
12237
12237
|
);
|
|
12238
12238
|
appendLog("info", `git-traces: uploaded ${filename} (${buffer.byteLength} bytes)`);
|
|
12239
12239
|
}
|
|
12240
|
-
|
|
12240
|
+
function pinEpochBaseline(repoRoot, sessionId, epoch) {
|
|
12241
|
+
const baselineSha = captureBaselineSha(repoRoot);
|
|
12242
|
+
pinRef(
|
|
12243
|
+
repoRoot,
|
|
12244
|
+
`refs/hillclimb/baseline/${sessionId}/${epochPrefix(epoch)}`,
|
|
12245
|
+
baselineSha
|
|
12246
|
+
);
|
|
12247
|
+
return { baselineSha, headSha: captureHeadSha(repoRoot) };
|
|
12248
|
+
}
|
|
12249
|
+
async function uploadEpochBaseline(params) {
|
|
12241
12250
|
const {
|
|
12242
12251
|
repoRoot,
|
|
12243
12252
|
client,
|
|
@@ -12245,16 +12254,12 @@ async function openEpoch(params) {
|
|
|
12245
12254
|
sessionId,
|
|
12246
12255
|
tool,
|
|
12247
12256
|
epoch,
|
|
12257
|
+
baselineSha,
|
|
12248
12258
|
prevHeadSha,
|
|
12249
|
-
transitionKind
|
|
12259
|
+
transitionKind,
|
|
12260
|
+
startedAt
|
|
12250
12261
|
} = params;
|
|
12251
|
-
const baselineSha = captureBaselineSha(repoRoot);
|
|
12252
12262
|
const prefix = epochPrefix(epoch);
|
|
12253
|
-
pinRef(
|
|
12254
|
-
repoRoot,
|
|
12255
|
-
`refs/hillclimb/baseline/${sessionId}/${prefix}`,
|
|
12256
|
-
baselineSha
|
|
12257
|
-
);
|
|
12258
12263
|
const metadata = buildBaselineMetadata(
|
|
12259
12264
|
repoRoot,
|
|
12260
12265
|
sessionId,
|
|
@@ -12263,7 +12268,8 @@ async function openEpoch(params) {
|
|
|
12263
12268
|
CLI_VERSION,
|
|
12264
12269
|
epoch,
|
|
12265
12270
|
prevHeadSha,
|
|
12266
|
-
transitionKind
|
|
12271
|
+
transitionKind,
|
|
12272
|
+
startedAt
|
|
12267
12273
|
);
|
|
12268
12274
|
let baselineTreeSha;
|
|
12269
12275
|
let bundleBuffer;
|
|
@@ -12297,11 +12303,17 @@ async function openEpoch(params) {
|
|
|
12297
12303
|
"application/x-git-bundle",
|
|
12298
12304
|
bundleBuffer
|
|
12299
12305
|
);
|
|
12300
|
-
return {
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
12306
|
+
return { baselineTreeSha };
|
|
12307
|
+
}
|
|
12308
|
+
async function openEpoch(params) {
|
|
12309
|
+
const { baselineSha, headSha } = pinEpochBaseline(
|
|
12310
|
+
params.repoRoot,
|
|
12311
|
+
params.sessionId,
|
|
12312
|
+
params.epoch
|
|
12313
|
+
);
|
|
12314
|
+
const uploaded = await uploadEpochBaseline({ ...params, baselineSha });
|
|
12315
|
+
if (!uploaded) return null;
|
|
12316
|
+
return { baselineSha, baselineTreeSha: uploaded.baselineTreeSha, headSha };
|
|
12305
12317
|
}
|
|
12306
12318
|
async function initializeSession(cwd, tool, sessionId) {
|
|
12307
12319
|
const project = await findProjectForCwd(cwd);
|
|
@@ -12309,57 +12321,25 @@ async function initializeSession(cwd, tool, sessionId) {
|
|
|
12309
12321
|
appendLog("warn", "git-traces: no hillclimb project config found, skipping");
|
|
12310
12322
|
return null;
|
|
12311
12323
|
}
|
|
12312
|
-
const {
|
|
12313
|
-
const identity = await loadIdentity(config.apiBaseUrl);
|
|
12314
|
-
if (!identity) {
|
|
12315
|
-
appendLog(
|
|
12316
|
-
"warn",
|
|
12317
|
-
`git-traces: no saved login for ${config.apiBaseUrl}, skipping`
|
|
12318
|
-
);
|
|
12319
|
-
return null;
|
|
12320
|
-
}
|
|
12321
|
-
const client = new PlatformClient(config.apiBaseUrl, identity.sessionCookie);
|
|
12322
|
-
const toolLabel = TOOL_LABELS[tool] ?? "Claude";
|
|
12323
|
-
const now = /* @__PURE__ */ new Date();
|
|
12324
|
-
const shortId = sessionId.slice(0, 12);
|
|
12325
|
-
const contribution = await client.createContribution(config.workspaceId, {
|
|
12326
|
-
contributionTypeSlug: GIT_TRACES_SLUG,
|
|
12327
|
-
title: `${toolLabel} session ${shortId} \u2014 ${formatTitleTimestamp2(now)}`,
|
|
12328
|
-
body: `Session ID: ${sessionId}
|
|
12329
|
-
Tool: ${toolLabel}
|
|
12330
|
-
Repo: ${cwd}
|
|
12331
|
-
Uploaded: ${now.toISOString()}`
|
|
12332
|
-
});
|
|
12333
|
-
await client.submitContribution(contribution.id);
|
|
12334
|
-
const artifacts = await openEpoch({
|
|
12335
|
-
repoRoot: cwd,
|
|
12336
|
-
client,
|
|
12337
|
-
contributionId: contribution.id,
|
|
12338
|
-
sessionId,
|
|
12339
|
-
tool,
|
|
12340
|
-
epoch: 1,
|
|
12341
|
-
prevHeadSha: null,
|
|
12342
|
-
transitionKind: "initial"
|
|
12343
|
-
});
|
|
12344
|
-
if (!artifacts) return null;
|
|
12324
|
+
const { baselineSha, headSha } = pinEpochBaseline(cwd, sessionId, 1);
|
|
12345
12325
|
const state = {
|
|
12346
12326
|
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
12347
12327
|
sessionId,
|
|
12348
|
-
contributionId:
|
|
12349
|
-
baselineSha
|
|
12350
|
-
baselineTreeSha:
|
|
12351
|
-
lastSnapshotSha:
|
|
12352
|
-
lastSnapshotTreeSha:
|
|
12328
|
+
contributionId: null,
|
|
12329
|
+
baselineSha,
|
|
12330
|
+
baselineTreeSha: null,
|
|
12331
|
+
lastSnapshotSha: baselineSha,
|
|
12332
|
+
lastSnapshotTreeSha: null,
|
|
12353
12333
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12354
12334
|
epoch: 1,
|
|
12355
12335
|
turnCount: 0,
|
|
12356
|
-
headSha
|
|
12336
|
+
headSha,
|
|
12357
12337
|
repoRoot: cwd
|
|
12358
12338
|
};
|
|
12359
12339
|
await writeSessionState(state, tool);
|
|
12360
12340
|
appendLog(
|
|
12361
12341
|
"info",
|
|
12362
|
-
`git-traces: session
|
|
12342
|
+
`git-traces: session pending (baseline=${baselineSha.slice(0, 8)}, awaiting first turn)`
|
|
12363
12343
|
);
|
|
12364
12344
|
return state;
|
|
12365
12345
|
}
|
|
@@ -12417,6 +12397,53 @@ async function handleStop(payload, tool) {
|
|
|
12417
12397
|
project.config.apiBaseUrl,
|
|
12418
12398
|
identity.sessionCookie
|
|
12419
12399
|
);
|
|
12400
|
+
if (state.contributionId === null) {
|
|
12401
|
+
const toolLabel = TOOL_LABELS[tool] ?? "Claude";
|
|
12402
|
+
const now = /* @__PURE__ */ new Date();
|
|
12403
|
+
const shortId = state.sessionId.slice(0, 12);
|
|
12404
|
+
const contribution = await client.createContribution(
|
|
12405
|
+
project.config.workspaceId,
|
|
12406
|
+
{
|
|
12407
|
+
contributionTypeSlug: GIT_TRACES_SLUG,
|
|
12408
|
+
title: `${toolLabel} session ${shortId} \u2014 ${formatTitleTimestamp2(now)}`,
|
|
12409
|
+
body: `Session ID: ${state.sessionId}
|
|
12410
|
+
Tool: ${toolLabel}
|
|
12411
|
+
Repo: ${cwd}
|
|
12412
|
+
Uploaded: ${now.toISOString()}`
|
|
12413
|
+
}
|
|
12414
|
+
);
|
|
12415
|
+
await client.submitContribution(contribution.id);
|
|
12416
|
+
const uploaded = await uploadEpochBaseline({
|
|
12417
|
+
repoRoot: cwd,
|
|
12418
|
+
client,
|
|
12419
|
+
contributionId: contribution.id,
|
|
12420
|
+
sessionId: state.sessionId,
|
|
12421
|
+
tool,
|
|
12422
|
+
epoch: 1,
|
|
12423
|
+
baselineSha: state.baselineSha,
|
|
12424
|
+
prevHeadSha: null,
|
|
12425
|
+
transitionKind: "initial",
|
|
12426
|
+
startedAt: state.startedAt
|
|
12427
|
+
});
|
|
12428
|
+
if (!uploaded) return;
|
|
12429
|
+
state.contributionId = contribution.id;
|
|
12430
|
+
state.baselineTreeSha = uploaded.baselineTreeSha;
|
|
12431
|
+
state.lastSnapshotTreeSha = uploaded.baselineTreeSha;
|
|
12432
|
+
await writeSessionState(state, tool);
|
|
12433
|
+
appendLog(
|
|
12434
|
+
"info",
|
|
12435
|
+
`git-traces: session registered on first turn (epoch=1, baseline=${state.baselineSha.slice(0, 8)}, contribution=${contribution.id})`
|
|
12436
|
+
);
|
|
12437
|
+
}
|
|
12438
|
+
const contributionId = state.contributionId;
|
|
12439
|
+
const lastSnapshotTreeSha = state.lastSnapshotTreeSha;
|
|
12440
|
+
if (contributionId === null || lastSnapshotTreeSha === null) {
|
|
12441
|
+
appendLog(
|
|
12442
|
+
"error",
|
|
12443
|
+
"git-traces: invariant violation \u2014 session state missing contributionId or tree SHA after lazy init"
|
|
12444
|
+
);
|
|
12445
|
+
return;
|
|
12446
|
+
}
|
|
12420
12447
|
const currentHeadSha = captureHeadSha(cwd);
|
|
12421
12448
|
if (currentHeadSha !== state.headSha) {
|
|
12422
12449
|
const transitionKind = detectTransitionKind(
|
|
@@ -12432,12 +12459,13 @@ async function handleStop(payload, tool) {
|
|
|
12432
12459
|
const artifacts = await openEpoch({
|
|
12433
12460
|
repoRoot: cwd,
|
|
12434
12461
|
client,
|
|
12435
|
-
contributionId
|
|
12462
|
+
contributionId,
|
|
12436
12463
|
sessionId: state.sessionId,
|
|
12437
12464
|
tool,
|
|
12438
12465
|
epoch: nextEpoch,
|
|
12439
12466
|
prevHeadSha: state.headSha || null,
|
|
12440
|
-
transitionKind
|
|
12467
|
+
transitionKind,
|
|
12468
|
+
startedAt: state.startedAt
|
|
12441
12469
|
});
|
|
12442
12470
|
if (!artifacts) return;
|
|
12443
12471
|
const next = {
|
|
@@ -12457,7 +12485,7 @@ async function handleStop(payload, tool) {
|
|
|
12457
12485
|
const currentTreeSha = buildSnapshotTree(cwd, currentSha);
|
|
12458
12486
|
const patchBuffer = createTreeDiffPatchGz(
|
|
12459
12487
|
cwd,
|
|
12460
|
-
|
|
12488
|
+
lastSnapshotTreeSha,
|
|
12461
12489
|
currentTreeSha
|
|
12462
12490
|
);
|
|
12463
12491
|
if (!patchBuffer) {
|
|
@@ -12478,7 +12506,7 @@ async function handleStop(payload, tool) {
|
|
|
12478
12506
|
const filename = `${prefix}-${turnLabel}-${recordedAt}.patch.gz`;
|
|
12479
12507
|
await uploadFile(
|
|
12480
12508
|
client,
|
|
12481
|
-
|
|
12509
|
+
contributionId,
|
|
12482
12510
|
filename,
|
|
12483
12511
|
"application/gzip",
|
|
12484
12512
|
patchBuffer
|