@yhong91/vibetime 0.1.57 → 0.1.59

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.
Files changed (2) hide show
  1. package/bin/vibetime.mjs +741 -87
  2. package/package.json +1 -1
package/bin/vibetime.mjs CHANGED
@@ -884,7 +884,7 @@ var init_esm = __esm({
884
884
 
885
885
  // src/cli.ts
886
886
  import { spawn as spawn2, spawnSync } from "node:child_process";
887
- import { mkdir as mkdir5, open, rm, stat as stat15, writeFile as writeFile4 } from "node:fs/promises";
887
+ import { mkdir as mkdir6, open, rm, stat as stat16, writeFile as writeFile5 } from "node:fs/promises";
888
888
  import os13 from "node:os";
889
889
  import path26 from "node:path";
890
890
  import { fileURLToPath } from "node:url";
@@ -2047,7 +2047,7 @@ function claudeStyleFileMetrics(tool, input) {
2047
2047
  }
2048
2048
 
2049
2049
  // src/lib/constants.ts
2050
- var PACKAGE_VERSION = true ? "0.1.57" : "0.1.1";
2050
+ var PACKAGE_VERSION = true ? "0.1.59" : "0.1.1";
2051
2051
  var GENERATED_MARKER = "Generated by vibetime.";
2052
2052
  var DEFAULT_API_URL = "http://121.196.224.82:3001";
2053
2053
  var DEFAULT_BACKFILL_BATCH_SIZE = 50;
@@ -5011,8 +5011,8 @@ async function codebuddyBackfillFiles(sourceRoot, home, env) {
5011
5011
  }
5012
5012
  const filePath = path9.join(traceDir, entry);
5013
5013
  try {
5014
- const stat16 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
5015
- files.push({ path: filePath, modifiedAt: stat16.mtime.toISOString(), groupId: pidDir.name });
5014
+ const stat17 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
5015
+ files.push({ path: filePath, modifiedAt: stat17.mtime.toISOString(), groupId: pidDir.name });
5016
5016
  } catch {
5017
5017
  }
5018
5018
  }
@@ -5099,7 +5099,7 @@ function fileActivitiesFromPatchChanges(changes, ts, cwd, displayFilePath3) {
5099
5099
  }
5100
5100
 
5101
5101
  // src/lib/session-context.ts
5102
- import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
5102
+ import { mkdir as mkdir3, stat as stat6, writeFile as writeFile3 } from "node:fs/promises";
5103
5103
  import os4 from "node:os";
5104
5104
  import path10 from "node:path";
5105
5105
  init_fs();
@@ -5126,32 +5126,86 @@ async function readPersistedSessionContext(home, sessionId) {
5126
5126
  }
5127
5127
  const cwd = typeof raw.cwd === "string" && raw.cwd.length > 0 ? raw.cwd : void 0;
5128
5128
  const project = typeof raw.project === "string" && raw.project.length > 0 ? raw.project : void 0;
5129
- return { version: SESSION_CONTEXT_VERSION, sessionId, cwd, project, updatedAt: raw.updatedAt };
5129
+ const usage = Array.isArray(raw.usage) ? raw.usage.filter((item) => {
5130
+ return isPlainObject(item) && typeof item.generationId === "string" && item.generationId.length > 0 && typeof item.ts === "string";
5131
+ }) : void 0;
5132
+ return { version: SESSION_CONTEXT_VERSION, sessionId, cwd, project, updatedAt: raw.updatedAt, usage };
5130
5133
  }
5131
5134
  async function readPersistedSessionContextFromOptions(options, sessionId) {
5132
5135
  return readPersistedSessionContext(resolveHome2(options), sessionId);
5133
5136
  }
5137
+ async function persistedSessionContextModifiedAt(home, sessionId) {
5138
+ const info = await stat6(sessionContextPath(home, sessionId)).catch(() => null);
5139
+ return info?.isFile() ? info.mtime : void 0;
5140
+ }
5134
5141
  async function persistHookSessionContext(home, payload) {
5135
5142
  if (!isPlainObject(payload)) {
5136
5143
  return;
5137
5144
  }
5138
- const sessionId = typeof payload.session_id === "string" ? payload.session_id.trim() : typeof payload.sessionId === "string" ? payload.sessionId.trim() : "";
5139
- const cwd = typeof payload.cwd === "string" ? payload.cwd.trim() : "";
5140
- if (!sessionId || !cwd || !path10.isAbsolute(cwd)) {
5145
+ const sessionId = hookSessionId(payload);
5146
+ const cwdRaw = typeof payload.cwd === "string" ? payload.cwd.trim() : "";
5147
+ const cwd = cwdRaw && path10.isAbsolute(cwdRaw) ? cwdRaw : void 0;
5148
+ const usage = hookUsageFromPayload(payload);
5149
+ if (!sessionId || !cwd && !usage) {
5141
5150
  return;
5142
5151
  }
5143
- const project = path10.basename(cwd) || void 0;
5152
+ const existing = await readPersistedSessionContext(home, sessionId);
5153
+ const nextUsage = mergeHookUsage(existing?.usage, usage);
5154
+ const nextCwd = cwd || existing?.cwd;
5144
5155
  const context = {
5145
5156
  version: SESSION_CONTEXT_VERSION,
5146
5157
  sessionId,
5147
- cwd,
5148
- project,
5149
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
5158
+ cwd: nextCwd,
5159
+ project: nextCwd ? path10.basename(nextCwd) : existing?.project,
5160
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
5161
+ usage: nextUsage
5150
5162
  };
5151
5163
  await mkdir3(sessionContextDir(home), { recursive: true });
5152
5164
  await writeFile3(sessionContextPath(home, sessionId), `${JSON.stringify(context, null, 2)}
5153
5165
  `, "utf8");
5154
5166
  }
5167
+ function hookSessionId(payload) {
5168
+ for (const key of ["session_id", "sessionId", "conversation_id", "conversationId"]) {
5169
+ const value = stringField(payload, key)?.trim();
5170
+ if (value) {
5171
+ return value;
5172
+ }
5173
+ }
5174
+ return "";
5175
+ }
5176
+ function hookUsageFromPayload(payload) {
5177
+ const nested = objectField(payload, "token_usage").inputTokens !== void 0 || objectField(payload, "tokenUsage").inputTokens !== void 0 ? { ...objectField(payload, "token_usage"), ...objectField(payload, "tokenUsage") } : payload;
5178
+ const tokensInput = numberField(nested, "input_tokens") ?? numberField(nested, "inputTokens") ?? numberField(payload, "input_tokens") ?? numberField(payload, "inputTokens");
5179
+ const tokensOutput = numberField(nested, "output_tokens") ?? numberField(nested, "outputTokens") ?? numberField(payload, "output_tokens") ?? numberField(payload, "outputTokens");
5180
+ const tokensCacheReadInput = numberField(nested, "cache_read_tokens") ?? numberField(nested, "cacheReadTokens") ?? numberField(payload, "cache_read_tokens") ?? numberField(payload, "cacheReadTokens");
5181
+ const tokensCacheCreationInput = numberField(nested, "cache_write_tokens") ?? numberField(nested, "cacheWriteTokens") ?? numberField(payload, "cache_write_tokens") ?? numberField(payload, "cacheWriteTokens");
5182
+ if (!tokensInput && !tokensOutput && !tokensCacheReadInput && !tokensCacheCreationInput) {
5183
+ return void 0;
5184
+ }
5185
+ const generationId = stringField(payload, "generation_id")?.trim() || stringField(payload, "generationId")?.trim() || stringField(payload, "request_id")?.trim() || stringField(payload, "requestId")?.trim() || `${tokensInput || 0}:${tokensOutput || 0}:${tokensCacheReadInput || 0}:${tokensCacheCreationInput || 0}`;
5186
+ return {
5187
+ generationId,
5188
+ ts: stringField(payload, "timestamp") || (/* @__PURE__ */ new Date()).toISOString(),
5189
+ model: stringField(payload, "model") || stringField(payload, "model_name"),
5190
+ tokensInput: tokensInput || void 0,
5191
+ tokensOutput: tokensOutput || void 0,
5192
+ tokensCacheReadInput: tokensCacheReadInput || void 0,
5193
+ tokensCacheCreationInput: tokensCacheCreationInput || void 0
5194
+ };
5195
+ }
5196
+ function mergeHookUsage(existing, next) {
5197
+ if (!next) {
5198
+ return existing;
5199
+ }
5200
+ const current = existing ? [...existing] : [];
5201
+ const index = current.findIndex((item) => item.generationId === next.generationId);
5202
+ if (index >= 0) {
5203
+ current[index] = next;
5204
+ } else {
5205
+ current.push(next);
5206
+ }
5207
+ return current;
5208
+ }
5155
5209
 
5156
5210
  // src/adapters/codex.ts
5157
5211
  async function parseCodexSessionFile(filePath, options) {
@@ -5841,7 +5895,7 @@ function createCodexAdapter() {
5841
5895
  }
5842
5896
 
5843
5897
  // src/adapters/copilot.ts
5844
- import { readdir as readdir5, readFile as readFile7, stat as stat6 } from "node:fs/promises";
5898
+ import { readdir as readdir5, readFile as readFile7, stat as stat7 } from "node:fs/promises";
5845
5899
  import os5 from "node:os";
5846
5900
  import path12 from "node:path";
5847
5901
  async function parseCopilotSessionFile(filePath, options) {
@@ -6182,7 +6236,7 @@ async function copilotBackfillFiles(sourceRoot, home = os5.homedir(), _env) {
6182
6236
  continue;
6183
6237
  }
6184
6238
  const eventsPath = path12.join(sessionDir, entry, "events.jsonl");
6185
- const info = await stat6(eventsPath).catch(() => null);
6239
+ const info = await stat7(eventsPath).catch(() => null);
6186
6240
  if (info) {
6187
6241
  results.push({ path: eventsPath, modifiedAt: info.mtime.toISOString() });
6188
6242
  }
@@ -6238,7 +6292,7 @@ function createCopilotAdapter() {
6238
6292
  }
6239
6293
 
6240
6294
  // src/adapters/cursor.ts
6241
- import { copyFile, readdir as readdir6, readFile as readFile8, stat as stat7 } from "node:fs/promises";
6295
+ import { copyFile, mkdir as mkdir4, readdir as readdir6, readFile as readFile8, stat as stat8, writeFile as writeFile4 } from "node:fs/promises";
6242
6296
  import os6 from "node:os";
6243
6297
  import path13 from "node:path";
6244
6298
 
@@ -6346,6 +6400,222 @@ function collectCursorHookCommands(content) {
6346
6400
 
6347
6401
  // src/adapters/cursor.ts
6348
6402
  init_fs();
6403
+
6404
+ // src/adapters/cursor-cloud-usage.ts
6405
+ var CURSOR_CLOUD_USAGE_GENERATION_ID = "cursor-cloud";
6406
+ var CURSOR_CLOUD_USAGE_FILENAME = "cursor-cloud-usage.json";
6407
+ var CURSOR_CLOUD_AGENT_PROJECT = "Cloud Agent";
6408
+ var CURSOR_DASHBOARD_USAGE_URL = "https://cursor.com/api/dashboard/get-filtered-usage-events";
6409
+ var CURSOR_CLOUD_USAGE_CACHE_VERSION = 1;
6410
+ var DEFAULT_WINDOW_DAYS = 90;
6411
+ var PAGE_SIZE = 1e3;
6412
+ function cursorCloudUsageDisabled(env = process.env) {
6413
+ const value = env.VIBETIME_CURSOR_CLOUD_USAGE;
6414
+ return value === "0" || value === "false";
6415
+ }
6416
+ function cursorSessionCookie(accessToken) {
6417
+ const token = accessToken.trim();
6418
+ if (!token) {
6419
+ return void 0;
6420
+ }
6421
+ const payload = jwtPayload(token);
6422
+ const userId = String(payload?.sub || "").split("|").at(-1)?.trim();
6423
+ if (!userId) {
6424
+ return void 0;
6425
+ }
6426
+ return `WorkosCursorSessionToken=${userId}%3A%3A${token}`;
6427
+ }
6428
+ function cursorCloudEventFromDashboard(event) {
6429
+ if (!isPlainObject(event)) {
6430
+ return void 0;
6431
+ }
6432
+ const conversationId = stringField(event, "conversationId")?.trim();
6433
+ if (!conversationId) {
6434
+ return void 0;
6435
+ }
6436
+ const tokenUsage = objectField(event, "tokenUsage");
6437
+ const tokensInput = numberField(tokenUsage, "inputTokens") || 0;
6438
+ const tokensOutput = numberField(tokenUsage, "outputTokens") || 0;
6439
+ const tokensCacheReadInput = numberField(tokenUsage, "cacheReadTokens") || 0;
6440
+ const tokensCacheCreationInput = numberField(tokenUsage, "cacheWriteTokens") || 0;
6441
+ if (tokensInput <= 0 && tokensOutput <= 0 && tokensCacheReadInput <= 0 && tokensCacheCreationInput <= 0) {
6442
+ return void 0;
6443
+ }
6444
+ return {
6445
+ conversationId,
6446
+ model: stringField(event, "model") || void 0,
6447
+ ts: timestampFrom(event.timestamp) || (/* @__PURE__ */ new Date(0)).toISOString(),
6448
+ tokensInput,
6449
+ tokensOutput,
6450
+ tokensCacheReadInput,
6451
+ tokensCacheCreationInput
6452
+ };
6453
+ }
6454
+ function groupCursorUsageEvents(events) {
6455
+ const byConversation = /* @__PURE__ */ new Map();
6456
+ for (const event of events) {
6457
+ const parsed = cursorCloudEventFromDashboard(event);
6458
+ if (!parsed) {
6459
+ continue;
6460
+ }
6461
+ const current = byConversation.get(parsed.conversationId);
6462
+ if (current) {
6463
+ current.push(parsed);
6464
+ } else {
6465
+ byConversation.set(parsed.conversationId, [parsed]);
6466
+ }
6467
+ }
6468
+ return byConversation;
6469
+ }
6470
+ function sumCursorCloudEvents(conversationId, events) {
6471
+ if (events.length === 0) {
6472
+ return void 0;
6473
+ }
6474
+ const first = events[0];
6475
+ const firstStart = first.startedAt || first.ts;
6476
+ const summed = {
6477
+ conversationId,
6478
+ model: first.model,
6479
+ ts: first.ts,
6480
+ startedAt: firstStart,
6481
+ tokensInput: 0,
6482
+ tokensOutput: 0,
6483
+ tokensCacheReadInput: 0,
6484
+ tokensCacheCreationInput: 0
6485
+ };
6486
+ for (const event of events) {
6487
+ summed.tokensInput += event.tokensInput;
6488
+ summed.tokensOutput += event.tokensOutput;
6489
+ summed.tokensCacheReadInput += event.tokensCacheReadInput;
6490
+ summed.tokensCacheCreationInput += event.tokensCacheCreationInput;
6491
+ const eventStart = event.startedAt || event.ts;
6492
+ if (Date.parse(eventStart) < Date.parse(summed.startedAt)) {
6493
+ summed.startedAt = eventStart;
6494
+ }
6495
+ if (Date.parse(event.ts) >= Date.parse(summed.ts)) {
6496
+ summed.ts = event.ts;
6497
+ if (event.model) {
6498
+ summed.model = event.model;
6499
+ }
6500
+ }
6501
+ }
6502
+ return summed;
6503
+ }
6504
+ function hookUncachedInputTokens(turn) {
6505
+ return Math.max(0, (turn.tokensInput || 0) - (turn.tokensCacheReadInput || 0));
6506
+ }
6507
+ function cloudEventMatchesHookTurn(event, turn) {
6508
+ return event.tokensInput === hookUncachedInputTokens(turn) && event.tokensOutput === (turn.tokensOutput || 0) && event.tokensCacheReadInput === (turn.tokensCacheReadInput || 0);
6509
+ }
6510
+ function unmatchedCloudEvents(events, turns) {
6511
+ const used = /* @__PURE__ */ new Set();
6512
+ for (const turn of turns) {
6513
+ const index = events.findIndex((event, i) => !used.has(i) && cloudEventMatchesHookTurn(event, turn));
6514
+ if (index >= 0) {
6515
+ used.add(index);
6516
+ }
6517
+ }
6518
+ return events.filter((_, i) => !used.has(i));
6519
+ }
6520
+ async function fetchCursorDashboardUsage(args) {
6521
+ const cookie = cursorSessionCookie(args.accessToken);
6522
+ if (!cookie) {
6523
+ return /* @__PURE__ */ new Map();
6524
+ }
6525
+ const fetchImpl = args.fetchImpl || fetch;
6526
+ const now = args.now || /* @__PURE__ */ new Date();
6527
+ const windowDays = args.windowDays ?? DEFAULT_WINDOW_DAYS;
6528
+ const pageSize = args.pageSize ?? PAGE_SIZE;
6529
+ const start = new Date(now.getTime() - windowDays * 24 * 60 * 60 * 1e3);
6530
+ const events = [];
6531
+ let page = 1;
6532
+ let total = Number.POSITIVE_INFINITY;
6533
+ while ((page - 1) * pageSize < total) {
6534
+ const body = JSON.stringify({
6535
+ page,
6536
+ pageSize,
6537
+ startDate: String(start.getTime()),
6538
+ endDate: String(now.getTime())
6539
+ });
6540
+ const response = await fetchImpl(CURSOR_DASHBOARD_USAGE_URL, {
6541
+ method: "POST",
6542
+ headers: {
6543
+ "Content-Type": "application/json",
6544
+ Accept: "application/json",
6545
+ Cookie: cookie,
6546
+ Origin: "https://cursor.com",
6547
+ Referer: "https://cursor.com/settings",
6548
+ "User-Agent": "vibetime-cli"
6549
+ },
6550
+ body,
6551
+ signal: AbortSignal.timeout(2e4)
6552
+ });
6553
+ if (!response.ok) {
6554
+ return /* @__PURE__ */ new Map();
6555
+ }
6556
+ const payload = await response.json();
6557
+ if (!isPlainObject(payload)) {
6558
+ return /* @__PURE__ */ new Map();
6559
+ }
6560
+ const pageEvents = Array.isArray(payload.usageEventsDisplay) ? payload.usageEventsDisplay : [];
6561
+ events.push(...pageEvents);
6562
+ const reported = numberField(payload, "totalUsageEventsCount");
6563
+ total = reported ?? pageEvents.length;
6564
+ if (pageEvents.length < pageSize) {
6565
+ break;
6566
+ }
6567
+ page += 1;
6568
+ }
6569
+ return groupCursorUsageEvents(events);
6570
+ }
6571
+ function serializeCursorCloudUsageCache(sessions) {
6572
+ const sorted = [...sessions].sort((a, b) => a.conversationId.localeCompare(b.conversationId));
6573
+ return `${JSON.stringify({ version: CURSOR_CLOUD_USAGE_CACHE_VERSION, sessions: sorted }, null, 2)}
6574
+ `;
6575
+ }
6576
+ function parseCursorCloudUsageCache(raw) {
6577
+ if (!isPlainObject(raw) || raw.version !== CURSOR_CLOUD_USAGE_CACHE_VERSION || !Array.isArray(raw.sessions)) {
6578
+ return [];
6579
+ }
6580
+ const sessions = [];
6581
+ for (const item of raw.sessions) {
6582
+ if (!isPlainObject(item)) {
6583
+ continue;
6584
+ }
6585
+ const conversationId = stringField(item, "conversationId")?.trim();
6586
+ const ts = stringField(item, "ts");
6587
+ const startedAt = stringField(item, "startedAt") || ts;
6588
+ if (!conversationId || !ts || !startedAt) {
6589
+ continue;
6590
+ }
6591
+ sessions.push({
6592
+ conversationId,
6593
+ model: stringField(item, "model"),
6594
+ ts,
6595
+ startedAt,
6596
+ tokensInput: numberField(item, "tokensInput") || 0,
6597
+ tokensOutput: numberField(item, "tokensOutput") || 0,
6598
+ tokensCacheReadInput: numberField(item, "tokensCacheReadInput") || 0,
6599
+ tokensCacheCreationInput: numberField(item, "tokensCacheCreationInput") || 0
6600
+ });
6601
+ }
6602
+ return sessions;
6603
+ }
6604
+ function jwtPayload(token) {
6605
+ const segment = token.split(".")[1];
6606
+ if (!segment) {
6607
+ return void 0;
6608
+ }
6609
+ try {
6610
+ const json = Buffer.from(segment, "base64url").toString("utf8");
6611
+ const parsed = JSON.parse(json);
6612
+ return isPlainObject(parsed) ? parsed : void 0;
6613
+ } catch {
6614
+ return void 0;
6615
+ }
6616
+ }
6617
+
6618
+ // src/adapters/cursor.ts
6349
6619
  var SOURCE_ID = "cursor";
6350
6620
  var AGENT_NAME = "cursor";
6351
6621
  var HOOK_COMMAND = `vibetime hook --agent ${SOURCE_ID}`;
@@ -6362,6 +6632,7 @@ var CURSOR_HOOK_EVENTS = [
6362
6632
  "subagentStart",
6363
6633
  "subagentStop",
6364
6634
  "stop",
6635
+ "afterAgentResponse",
6365
6636
  "afterFileEdit",
6366
6637
  "afterShellExecution",
6367
6638
  "preCompact"
@@ -6413,6 +6684,297 @@ function cursorStateDbCandidates(home, env) {
6413
6684
  }
6414
6685
  return candidates;
6415
6686
  }
6687
+ var cursorCloudUsageByOptions = /* @__PURE__ */ new WeakMap();
6688
+ function isCursorStateDbPath(filePath) {
6689
+ if (!filePath) {
6690
+ return false;
6691
+ }
6692
+ const base = path13.basename(filePath);
6693
+ return base === "state.vscdb" || base.endsWith(".vscdb");
6694
+ }
6695
+ function injectedCloudEvent(conversationId, item) {
6696
+ const tokensInput = numberField(item, "tokensInput") || numberField(item, "inputTokens") || 0;
6697
+ const tokensOutput = numberField(item, "tokensOutput") || numberField(item, "outputTokens") || 0;
6698
+ const tokensCacheReadInput = numberField(item, "tokensCacheReadInput") || numberField(item, "cacheReadTokens") || 0;
6699
+ const tokensCacheCreationInput = numberField(item, "tokensCacheCreationInput") || numberField(item, "cacheWriteTokens") || 0;
6700
+ if (tokensInput <= 0 && tokensOutput <= 0 && tokensCacheReadInput <= 0 && tokensCacheCreationInput <= 0) {
6701
+ return void 0;
6702
+ }
6703
+ const ts = stringField(item, "ts") || (/* @__PURE__ */ new Date(0)).toISOString();
6704
+ return {
6705
+ conversationId,
6706
+ model: stringField(item, "model"),
6707
+ ts,
6708
+ startedAt: stringField(item, "startedAt") || void 0,
6709
+ tokensInput,
6710
+ tokensOutput,
6711
+ tokensCacheReadInput,
6712
+ tokensCacheCreationInput
6713
+ };
6714
+ }
6715
+ function injectedCursorCloudUsage(options) {
6716
+ if (!Object.prototype.hasOwnProperty.call(options, "cursorCloudUsage")) {
6717
+ return void 0;
6718
+ }
6719
+ const raw = options.cursorCloudUsage;
6720
+ const map = /* @__PURE__ */ new Map();
6721
+ if (!isPlainObject(raw)) {
6722
+ return map;
6723
+ }
6724
+ for (const [conversationId, item] of Object.entries(raw)) {
6725
+ if (!conversationId) {
6726
+ continue;
6727
+ }
6728
+ const events = [];
6729
+ if (Array.isArray(item)) {
6730
+ for (const entry of item) {
6731
+ if (isPlainObject(entry)) {
6732
+ const parsed = injectedCloudEvent(conversationId, entry);
6733
+ if (parsed) {
6734
+ events.push(parsed);
6735
+ }
6736
+ }
6737
+ }
6738
+ } else if (isPlainObject(item) && Array.isArray(item.events)) {
6739
+ for (const entry of item.events) {
6740
+ if (isPlainObject(entry)) {
6741
+ const parsed = injectedCloudEvent(conversationId, entry);
6742
+ if (parsed) {
6743
+ events.push(parsed);
6744
+ }
6745
+ }
6746
+ }
6747
+ } else if (isPlainObject(item)) {
6748
+ const parsed = injectedCloudEvent(conversationId, item);
6749
+ if (parsed) {
6750
+ events.push(parsed);
6751
+ }
6752
+ }
6753
+ if (events.length > 0) {
6754
+ map.set(conversationId, events);
6755
+ }
6756
+ }
6757
+ return map;
6758
+ }
6759
+ async function readCursorAccessToken(dbPath) {
6760
+ if (!isCursorStateDbPath(dbPath)) {
6761
+ return void 0;
6762
+ }
6763
+ const info = await stat8(dbPath).catch(() => null);
6764
+ if (!info) {
6765
+ return void 0;
6766
+ }
6767
+ try {
6768
+ const opened = await openCursorDb(dbPath);
6769
+ try {
6770
+ const row = opened.db.prepare(
6771
+ "SELECT value FROM ItemTable WHERE key = 'cursorAuth/accessToken'"
6772
+ ).get();
6773
+ const token = decodeKv(row?.value)?.trim();
6774
+ return token || void 0;
6775
+ } finally {
6776
+ opened.db.close();
6777
+ await opened.cleanup();
6778
+ }
6779
+ } catch {
6780
+ return void 0;
6781
+ }
6782
+ }
6783
+ async function loadCursorCloudUsageMap(options, filePath) {
6784
+ const injected = injectedCursorCloudUsage(options);
6785
+ if (injected) {
6786
+ return injected;
6787
+ }
6788
+ if (cursorCloudUsageDisabled()) {
6789
+ return /* @__PURE__ */ new Map();
6790
+ }
6791
+ const cached = cursorCloudUsageByOptions.get(options);
6792
+ if (cached) {
6793
+ return cached;
6794
+ }
6795
+ const pending = (async () => {
6796
+ try {
6797
+ const token = isCursorStateDbPath(filePath) ? await readCursorAccessToken(filePath) : await readCursorAccessTokenFromHome(options);
6798
+ if (!token) {
6799
+ return /* @__PURE__ */ new Map();
6800
+ }
6801
+ const fetchImpl = typeof options.cursorCloudFetch === "function" ? options.cursorCloudFetch : void 0;
6802
+ return await fetchCursorDashboardUsage({ accessToken: token, fetchImpl });
6803
+ } catch {
6804
+ return /* @__PURE__ */ new Map();
6805
+ }
6806
+ })();
6807
+ cursorCloudUsageByOptions.set(options, pending);
6808
+ return pending;
6809
+ }
6810
+ async function readCursorAccessTokenFromHome(options) {
6811
+ const home = stringOption(options.home) || os6.homedir();
6812
+ const env = {
6813
+ CURSOR_HOME: process.env.CURSOR_HOME,
6814
+ CURSOR_USER_DIR: process.env.CURSOR_USER_DIR
6815
+ };
6816
+ for (const candidate of cursorStateDbCandidates(home, env)) {
6817
+ const token = await readCursorAccessToken(candidate);
6818
+ if (token) {
6819
+ return token;
6820
+ }
6821
+ }
6822
+ return void 0;
6823
+ }
6824
+ function cloudUsageToPersisted(row, index = 0) {
6825
+ return {
6826
+ generationId: `${CURSOR_CLOUD_USAGE_GENERATION_ID}:${index}:${row.tokensInput}:${row.tokensOutput}:${row.tokensCacheReadInput}`,
6827
+ ts: row.ts,
6828
+ model: row.model,
6829
+ tokensInput: row.tokensInput || void 0,
6830
+ tokensOutput: row.tokensOutput || void 0,
6831
+ tokensCacheReadInput: row.tokensCacheReadInput || void 0,
6832
+ tokensCacheCreationInput: row.tokensCacheCreationInput || void 0
6833
+ };
6834
+ }
6835
+ async function resolveCursorSessionUsage(options, sessionId, filePath) {
6836
+ const persisted = await readPersistedSessionContextFromOptions(options, sessionId);
6837
+ const cloudEvents = (await loadCursorCloudUsageMap(options, filePath)).get(sessionId) || [];
6838
+ if (persisted?.usage?.length) {
6839
+ return [
6840
+ ...persisted.usage,
6841
+ ...unmatchedCloudEvents(cloudEvents, persisted.usage).map((event, index) => cloudUsageToPersisted(event, index))
6842
+ ];
6843
+ }
6844
+ return cloudEvents.map((event, index) => cloudUsageToPersisted(event, index));
6845
+ }
6846
+ function cursorCloudUsageCachePath(home) {
6847
+ return path13.join(home, ".vibetime", CURSOR_CLOUD_USAGE_FILENAME);
6848
+ }
6849
+ async function listLocalCursorSessionIds(home, env) {
6850
+ const ids = /* @__PURE__ */ new Set();
6851
+ for (const dbPath of cursorStateDbCandidates(home, env)) {
6852
+ const composerIds = await listCursorComposerIds(dbPath);
6853
+ for (const id of composerIds) {
6854
+ ids.add(id);
6855
+ }
6856
+ if (composerIds.size > 0 || await stat8(dbPath).catch(() => null)) {
6857
+ break;
6858
+ }
6859
+ }
6860
+ const projects = cursorProjectsDir(home, env);
6861
+ for (const filePath of await listJsonlFiles(projects)) {
6862
+ if (!isCursorTranscriptPath(filePath) && path13.basename(path13.dirname(filePath)) !== "agent-transcripts") {
6863
+ continue;
6864
+ }
6865
+ const sessionId = path13.basename(filePath, ".jsonl");
6866
+ if (sessionId) {
6867
+ ids.add(sessionId);
6868
+ }
6869
+ }
6870
+ return ids;
6871
+ }
6872
+ async function writeCursorCloudUsageCache(cachePath, sessions) {
6873
+ const next = serializeCursorCloudUsageCache(sessions);
6874
+ const existing = await readFile8(cachePath, "utf8").catch(() => "");
6875
+ if (existing === next) {
6876
+ const info2 = await stat8(cachePath);
6877
+ return info2.mtime.toISOString();
6878
+ }
6879
+ await mkdir4(path13.dirname(cachePath), { recursive: true });
6880
+ await writeFile4(cachePath, next, "utf8");
6881
+ const info = await stat8(cachePath);
6882
+ return info.mtime.toISOString();
6883
+ }
6884
+ async function appendCursorCloudAgentSource(files, home, env, options) {
6885
+ const dbPath = files.find((file) => isCursorStateDbPath(file.path))?.path;
6886
+ const map = await loadCursorCloudUsageMap(options, dbPath);
6887
+ const localIds = await listLocalCursorSessionIds(home, env);
6888
+ const cloudOnly = [];
6889
+ for (const [conversationId, events] of map) {
6890
+ if (localIds.has(conversationId)) {
6891
+ continue;
6892
+ }
6893
+ const summed = sumCursorCloudEvents(conversationId, events);
6894
+ if (summed) {
6895
+ cloudOnly.push(summed);
6896
+ }
6897
+ }
6898
+ if (cloudOnly.length === 0) {
6899
+ return;
6900
+ }
6901
+ const cachePath = cursorCloudUsageCachePath(home);
6902
+ files.push({
6903
+ path: cachePath,
6904
+ modifiedAt: await writeCursorCloudUsageCache(cachePath, cloudOnly)
6905
+ });
6906
+ }
6907
+ function parseCursorCloudAgentSessions(filePath, options, sessions, localIds) {
6908
+ const events = [];
6909
+ const sourcePathHash = `sha256:${createStableHash(filePath)}`;
6910
+ const project = CURSOR_CLOUD_AGENT_PROJECT;
6911
+ const workspaceId = createWorkspaceId({ projectName: project });
6912
+ let lineNumber = 0;
6913
+ for (const row of sessions) {
6914
+ if (localIds.has(row.conversationId) || !row.startedAt) {
6915
+ continue;
6916
+ }
6917
+ const sessionId = row.conversationId;
6918
+ const model = row.model;
6919
+ const endedAt = row.ts || row.startedAt;
6920
+ const push = (partial, topType) => {
6921
+ lineNumber += 1;
6922
+ const event = {
6923
+ schemaVersion: AGENT_TIME_SCHEMA_VERSION,
6924
+ source: SOURCE_ID,
6925
+ agent: AGENT_NAME,
6926
+ workspaceId,
6927
+ project,
6928
+ model,
6929
+ sessionId,
6930
+ ...partial
6931
+ };
6932
+ events.push(withBackfillRefs(event, {
6933
+ filePath,
6934
+ sourcePathHash,
6935
+ lineNumber,
6936
+ topType,
6937
+ payloadType: event.type,
6938
+ options
6939
+ }));
6940
+ };
6941
+ push({
6942
+ ts: row.startedAt,
6943
+ type: "session.started",
6944
+ confidence: "partial",
6945
+ refs: stringRefs({ sourceId: `${sessionId}:started` })
6946
+ }, "cloud-agent");
6947
+ emitPersistedCursorUsage(
6948
+ push,
6949
+ [cloudUsageToPersisted(row)],
6950
+ endedAt,
6951
+ void 0,
6952
+ model,
6953
+ sessionId
6954
+ );
6955
+ push({
6956
+ ts: endedAt,
6957
+ type: "session.ended",
6958
+ confidence: "partial",
6959
+ refs: stringRefs({ sourceId: `${sessionId}:ended` })
6960
+ }, "cloud-agent");
6961
+ }
6962
+ return events;
6963
+ }
6964
+ async function parseCursorCloudAgentFile(filePath, options) {
6965
+ const home = stringOption(options.home) || os6.homedir();
6966
+ const env = {
6967
+ CURSOR_HOME: process.env.CURSOR_HOME,
6968
+ CURSOR_USER_DIR: process.env.CURSOR_USER_DIR
6969
+ };
6970
+ const injected = injectedCursorCloudUsage(options);
6971
+ const sessions = injected ? [...injected.entries()].flatMap(([conversationId, events]) => {
6972
+ const summed = sumCursorCloudEvents(conversationId, events);
6973
+ return summed ? [summed] : [];
6974
+ }) : parseCursorCloudUsageCache(await readJsonIfExists(filePath));
6975
+ const localIds = await listLocalCursorSessionIds(home, env);
6976
+ return parseCursorCloudAgentSessions(filePath, options, sessions, localIds);
6977
+ }
6416
6978
  function decodeKv(value) {
6417
6979
  if (typeof value === "string") {
6418
6980
  return value;
@@ -6778,7 +7340,7 @@ async function buildCursorChatMetaIndex(cursorHome2) {
6778
7340
  return index;
6779
7341
  }
6780
7342
  async function readCursorStoreMeta(storePath) {
6781
- const info = await stat7(storePath).catch(() => null);
7343
+ const info = await stat8(storePath).catch(() => null);
6782
7344
  if (!info) {
6783
7345
  return {};
6784
7346
  }
@@ -6851,7 +7413,7 @@ async function parseCursorTranscriptFile(filePath, options) {
6851
7413
  }
6852
7414
  const sessionId = path13.basename(filePath, ".jsonl");
6853
7415
  const context = await resolveTranscriptContext(filePath, sessionId);
6854
- const fileInfo = await stat7(filePath).catch(() => null);
7416
+ const fileInfo = await stat8(filePath).catch(() => null);
6855
7417
  let cwd = context.cwd;
6856
7418
  let project = context.project;
6857
7419
  let model = context.model;
@@ -7062,6 +7624,7 @@ async function parseCursorTranscriptFile(filePath, options) {
7062
7624
  }
7063
7625
  }
7064
7626
  const endedAt = latestTimestamp(lastTs, context.endedAt, fileMtime);
7627
+ const lastTurnId = currentTurnId;
7065
7628
  if (endedAt) {
7066
7629
  closeTurn(endedAt);
7067
7630
  if (sessionStarted) {
@@ -7073,11 +7636,60 @@ async function parseCursorTranscriptFile(filePath, options) {
7073
7636
  }, "transcript");
7074
7637
  }
7075
7638
  }
7639
+ await appendPersistedCursorUsage({
7640
+ push,
7641
+ sessionId,
7642
+ options,
7643
+ filePath,
7644
+ fallbackTs: endedAt || lastTs,
7645
+ lastTurnId,
7646
+ model,
7647
+ skip: events.some((event) => event.type === "model.usage")
7648
+ });
7076
7649
  return events;
7077
7650
  }
7651
+ async function appendPersistedCursorUsage(args) {
7652
+ if (args.skip) {
7653
+ return;
7654
+ }
7655
+ emitPersistedCursorUsage(
7656
+ args.push,
7657
+ await resolveCursorSessionUsage(args.options, args.sessionId, args.filePath),
7658
+ args.fallbackTs,
7659
+ args.lastTurnId,
7660
+ args.model,
7661
+ args.sessionId
7662
+ );
7663
+ }
7664
+ function emitPersistedCursorUsage(push, usage, fallbackTs, lastTurnId, model, sessionId, skip = false) {
7665
+ if (skip || !usage?.length || !fallbackTs) {
7666
+ return;
7667
+ }
7668
+ for (const item of usage) {
7669
+ const metrics = usageMetrics({
7670
+ inputTokens: item.tokensInput,
7671
+ outputTokens: item.tokensOutput,
7672
+ cacheReadTokens: item.tokensCacheReadInput,
7673
+ cacheWriteTokens: item.tokensCacheCreationInput
7674
+ });
7675
+ if (!metrics) {
7676
+ continue;
7677
+ }
7678
+ const fromCloud = item.generationId.startsWith(CURSOR_CLOUD_USAGE_GENERATION_ID);
7679
+ push({
7680
+ ts: timestampFrom(item.ts) || fallbackTs,
7681
+ type: "model.usage",
7682
+ turnId: lastTurnId,
7683
+ model: item.model || model,
7684
+ confidence: fromCloud ? "partial" : "exact",
7685
+ metrics,
7686
+ refs: stringRefs({ sourceId: `${sessionId}:${item.generationId}:usage` })
7687
+ }, fromCloud ? "cloud-usage" : "hook-usage");
7688
+ }
7689
+ }
7078
7690
  async function listCursorComposerIds(dbPath) {
7079
7691
  const ids = /* @__PURE__ */ new Set();
7080
- const info = await stat7(dbPath).catch(() => null);
7692
+ const info = await stat8(dbPath).catch(() => null);
7081
7693
  if (!info) {
7082
7694
  return ids;
7083
7695
  }
@@ -7096,7 +7708,17 @@ async function listCursorComposerIds(dbPath) {
7096
7708
  }
7097
7709
  return ids;
7098
7710
  }
7099
- async function collectCursorTranscriptFiles(root, skipSessionIds) {
7711
+ async function modifiedAtWithHookUsage(fileMtime, home, sessionIds) {
7712
+ let latest = fileMtime.getTime();
7713
+ for (const sessionId of sessionIds) {
7714
+ const extra = await persistedSessionContextModifiedAt(home, sessionId);
7715
+ if (extra && extra.getTime() > latest) {
7716
+ latest = extra.getTime();
7717
+ }
7718
+ }
7719
+ return new Date(latest).toISOString();
7720
+ }
7721
+ async function collectCursorTranscriptFiles(root, home, skipSessionIds) {
7100
7722
  const files = [];
7101
7723
  const seen = /* @__PURE__ */ new Set();
7102
7724
  for (const filePath of await listJsonlFiles(root)) {
@@ -7108,16 +7730,22 @@ async function collectCursorTranscriptFiles(root, skipSessionIds) {
7108
7730
  continue;
7109
7731
  }
7110
7732
  seen.add(sessionId);
7111
- const info = await stat7(filePath).catch(() => null);
7733
+ const info = await stat8(filePath).catch(() => null);
7112
7734
  if (!info) {
7113
7735
  continue;
7114
7736
  }
7115
- files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
7737
+ files.push({
7738
+ path: filePath,
7739
+ modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, [sessionId])
7740
+ });
7116
7741
  }
7117
7742
  return files;
7118
7743
  }
7119
7744
  async function parseCursorSessionFile(filePath, options) {
7120
7745
  const base = path13.basename(filePath);
7746
+ if (base === CURSOR_CLOUD_USAGE_FILENAME) {
7747
+ return parseCursorCloudAgentFile(filePath, options);
7748
+ }
7121
7749
  if (base.endsWith(".jsonl")) {
7122
7750
  return parseCursorTranscriptFile(filePath, options);
7123
7751
  }
@@ -7133,7 +7761,8 @@ async function parseCursorSessionFile(filePath, options) {
7133
7761
  try {
7134
7762
  const composers = listComposers(opened.db);
7135
7763
  for (const composer of composers) {
7136
- events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options));
7764
+ const usage = await resolveCursorSessionUsage(options, composer.composerId, filePath);
7765
+ events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options, usage));
7137
7766
  }
7138
7767
  } finally {
7139
7768
  opened.db.close();
@@ -7141,7 +7770,7 @@ async function parseCursorSessionFile(filePath, options) {
7141
7770
  }
7142
7771
  return events;
7143
7772
  }
7144
- function parseComposer(db, composer, filePath, sourcePathHash, options) {
7773
+ function parseComposer(db, composer, filePath, sourcePathHash, options, persistedUsage) {
7145
7774
  const sessionId = composer.composerId;
7146
7775
  const data = composer.data;
7147
7776
  const { cwd, project } = workspaceFromComposer(data);
@@ -7351,54 +7980,79 @@ function parseComposer(db, composer, filePath, sourcePathHash, options) {
7351
7980
  refs: stringRefs({ sourceId: `${sessionId}:ended` })
7352
7981
  }, "composer");
7353
7982
  }
7983
+ emitPersistedCursorUsage(push, persistedUsage, endedAt || startedAt, currentTurnId, model, sessionId, events.some((event) => event.type === "model.usage"));
7354
7984
  return events;
7355
7985
  }
7356
- async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
7986
+ async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env, options) {
7357
7987
  if (sourceRoot) {
7358
- const info = await stat7(sourceRoot).catch(() => null);
7988
+ const info = await stat8(sourceRoot).catch(() => null);
7359
7989
  if (!info) {
7360
7990
  return [];
7361
7991
  }
7362
7992
  if (info.isDirectory()) {
7363
7993
  const files2 = [];
7994
+ const skipIds2 = /* @__PURE__ */ new Set();
7364
7995
  for (const dbPath of await listFilesByExtensions(sourceRoot, [".vscdb", ".db"])) {
7365
7996
  const base = path13.basename(dbPath);
7366
7997
  if (base === "store.db" || base !== "state.vscdb" && !base.endsWith(".vscdb")) {
7367
7998
  continue;
7368
7999
  }
7369
- const dbInfo = await stat7(dbPath).catch(() => null);
7370
- if (dbInfo) {
7371
- files2.push({ path: dbPath, modifiedAt: dbInfo.mtime.toISOString() });
8000
+ const dbInfo = await stat8(dbPath).catch(() => null);
8001
+ if (!dbInfo) {
8002
+ continue;
7372
8003
  }
7373
- }
7374
- const skipIds2 = /* @__PURE__ */ new Set();
7375
- for (const file of files2) {
7376
- for (const id of await listCursorComposerIds(file.path)) {
8004
+ const composerIds2 = await listCursorComposerIds(dbPath);
8005
+ for (const id of composerIds2) {
7377
8006
  skipIds2.add(id);
7378
8007
  }
8008
+ files2.push({
8009
+ path: dbPath,
8010
+ modifiedAt: await modifiedAtWithHookUsage(dbInfo.mtime, home, composerIds2)
8011
+ });
7379
8012
  }
7380
- files2.push(...await collectCursorTranscriptFiles(sourceRoot, skipIds2));
8013
+ files2.push(...await collectCursorTranscriptFiles(sourceRoot, home, skipIds2));
7381
8014
  return files2;
7382
8015
  }
7383
8016
  if (path13.basename(sourceRoot) === "store.db") {
7384
8017
  return [];
7385
8018
  }
7386
- return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
8019
+ if (sourceRoot.endsWith(".jsonl")) {
8020
+ return [{
8021
+ path: sourceRoot,
8022
+ modifiedAt: await modifiedAtWithHookUsage(
8023
+ info.mtime,
8024
+ home,
8025
+ [path13.basename(sourceRoot, ".jsonl")]
8026
+ )
8027
+ }];
8028
+ }
8029
+ const composerIds = await listCursorComposerIds(sourceRoot);
8030
+ return [{
8031
+ path: sourceRoot,
8032
+ modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, composerIds)
8033
+ }];
7387
8034
  }
7388
8035
  const files = [];
7389
8036
  const skipIds = /* @__PURE__ */ new Set();
7390
8037
  for (const candidatePath of cursorStateDbCandidates(home, env)) {
7391
- const info = await stat7(candidatePath).catch(() => null);
8038
+ const info = await stat8(candidatePath).catch(() => null);
7392
8039
  if (!info) {
7393
8040
  continue;
7394
8041
  }
7395
- files.push({ path: candidatePath, modifiedAt: info.mtime.toISOString() });
7396
- for (const id of await listCursorComposerIds(candidatePath)) {
8042
+ const composerIds = await listCursorComposerIds(candidatePath);
8043
+ for (const id of composerIds) {
7397
8044
  skipIds.add(id);
7398
8045
  }
8046
+ files.push({
8047
+ path: candidatePath,
8048
+ modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, composerIds)
8049
+ });
7399
8050
  break;
7400
8051
  }
7401
- files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), skipIds));
8052
+ files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), home, skipIds));
8053
+ if (options) {
8054
+ await appendCursorCloudAgentSource(files, home, env, options);
8055
+ }
7402
8056
  return files;
7403
8057
  }
7404
8058
  function cursorHookConfig() {
@@ -7444,7 +8098,7 @@ function createCursorAdapter() {
7444
8098
  }
7445
8099
 
7446
8100
  // src/adapters/grok-build.ts
7447
- import { readdir as readdir7, readFile as readFile9, stat as stat8 } from "node:fs/promises";
8101
+ import { readdir as readdir7, readFile as readFile9, stat as stat9 } from "node:fs/promises";
7448
8102
  import path14 from "node:path";
7449
8103
  init_fs();
7450
8104
  var GROK_COST_TICKS_PER_USD = 1e9;
@@ -7484,7 +8138,7 @@ async function grokBackfillFiles(sourceRoot, home, env) {
7484
8138
  continue;
7485
8139
  }
7486
8140
  try {
7487
- const info = await stat8(entryPath);
8141
+ const info = await stat9(entryPath);
7488
8142
  files.push({ path: entryPath, modifiedAt: info.mtime.toISOString() });
7489
8143
  } catch {
7490
8144
  }
@@ -9244,19 +9898,19 @@ function opencodeDataCandidates(home, env) {
9244
9898
  return [primary, path16.join(home, ".opencode", "opencode.db")];
9245
9899
  }
9246
9900
  async function opencodeBackfillFiles(sourceRoot, home = os7.homedir(), env) {
9247
- const { stat: stat16 } = await import("node:fs/promises");
9901
+ const { stat: stat17 } = await import("node:fs/promises");
9248
9902
  if (sourceRoot) {
9249
9903
  if (!sourceRoot.endsWith(".db")) {
9250
9904
  return [];
9251
9905
  }
9252
- const info = await stat16(sourceRoot).catch(() => null);
9906
+ const info = await stat17(sourceRoot).catch(() => null);
9253
9907
  if (!info) {
9254
9908
  return [];
9255
9909
  }
9256
9910
  return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
9257
9911
  }
9258
9912
  for (const candidatePath of opencodeDataCandidates(home, env)) {
9259
- const info = await stat16(candidatePath).catch(() => null);
9913
+ const info = await stat17(candidatePath).catch(() => null);
9260
9914
  if (info) {
9261
9915
  return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
9262
9916
  }
@@ -9356,7 +10010,7 @@ function createOpenCodeAdapter() {
9356
10010
  }
9357
10011
 
9358
10012
  // src/adapters/pi.ts
9359
- import { readdir as readdir8, readFile as readFile11, stat as stat9 } from "node:fs/promises";
10013
+ import { readdir as readdir8, readFile as readFile11, stat as stat10 } from "node:fs/promises";
9360
10014
  import os8 from "node:os";
9361
10015
  import path17 from "node:path";
9362
10016
  init_fs();
@@ -10052,7 +10706,7 @@ async function piBackfillFiles(sourceRoot, home = os8.homedir(), env) {
10052
10706
  ]);
10053
10707
  const files = lists.flat().sort();
10054
10708
  return Promise.all(files.map(async (filePath) => {
10055
- const info = await stat9(filePath);
10709
+ const info = await stat10(filePath);
10056
10710
  let groupId = resolvePiBackfillGroupId(filePath);
10057
10711
  if (!groupId && filePath.endsWith(".json")) {
10058
10712
  groupId = await workflowRunGroupId(filePath);
@@ -10099,7 +10753,7 @@ function createPiAdapter() {
10099
10753
  }
10100
10754
 
10101
10755
  // src/adapters/qoder-cn.ts
10102
- import { readdir as readdir9, readFile as readFile12, stat as stat10 } from "node:fs/promises";
10756
+ import { readdir as readdir9, readFile as readFile12, stat as stat11 } from "node:fs/promises";
10103
10757
  import os10 from "node:os";
10104
10758
  import path19 from "node:path";
10105
10759
 
@@ -10996,7 +11650,7 @@ async function gitRootFromCwds2(cwds) {
10996
11650
  while (!seen.has(current)) {
10997
11651
  seen.add(current);
10998
11652
  try {
10999
- await stat10(path19.join(current, ".git"));
11653
+ await stat11(path19.join(current, ".git"));
11000
11654
  return current;
11001
11655
  } catch {
11002
11656
  }
@@ -11146,7 +11800,7 @@ function createQoderCnAdapter() {
11146
11800
 
11147
11801
  // src/adapters/qoder.ts
11148
11802
  import { existsSync } from "node:fs";
11149
- import { readdir as readdir10, readFile as readFile13, stat as stat11 } from "node:fs/promises";
11803
+ import { readdir as readdir10, readFile as readFile13, stat as stat12 } from "node:fs/promises";
11150
11804
  import os11 from "node:os";
11151
11805
  import path20 from "node:path";
11152
11806
  function parseQoderPaths(filePath) {
@@ -11842,7 +12496,7 @@ async function gitRootFromCwds3(cwds) {
11842
12496
  while (!seen.has(current)) {
11843
12497
  seen.add(current);
11844
12498
  try {
11845
- await stat11(path20.join(current, ".git"));
12499
+ await stat12(path20.join(current, ".git"));
11846
12500
  return current;
11847
12501
  } catch {
11848
12502
  }
@@ -12050,7 +12704,7 @@ function normalizeId(id) {
12050
12704
  }
12051
12705
 
12052
12706
  // src/adapters/workbuddy.ts
12053
- import { readdir as readdir11, readFile as readFile14, stat as stat12 } from "node:fs/promises";
12707
+ import { readdir as readdir11, readFile as readFile14, stat as stat13 } from "node:fs/promises";
12054
12708
  import path21 from "node:path";
12055
12709
  function workbuddyProjectsDir(home, env) {
12056
12710
  const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
@@ -12503,7 +13157,7 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
12503
13157
  for (const entry of entries) {
12504
13158
  if (entry.isFile() && entry.name.endsWith(".jsonl")) {
12505
13159
  const filePath = path21.join(projectDir, entry.name);
12506
- const info = await stat12(filePath);
13160
+ const info = await stat13(filePath);
12507
13161
  files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
12508
13162
  }
12509
13163
  }
@@ -12564,7 +13218,7 @@ function createWorkbuddyAdapter() {
12564
13218
 
12565
13219
  // src/adapters/zcode.ts
12566
13220
  import { execFile } from "node:child_process";
12567
- import { readFile as readFile15, stat as stat13 } from "node:fs/promises";
13221
+ import { readFile as readFile15, stat as stat14 } from "node:fs/promises";
12568
13222
  import path22 from "node:path";
12569
13223
  import { promisify as promisify2 } from "node:util";
12570
13224
  init_fs();
@@ -12583,7 +13237,7 @@ var providerNameCache = null;
12583
13237
  async function loadProviderNames(configPath2) {
12584
13238
  let fileMtime = 0;
12585
13239
  try {
12586
- const info = await stat13(configPath2);
13240
+ const info = await stat14(configPath2);
12587
13241
  fileMtime = info.mtimeMs;
12588
13242
  } catch {
12589
13243
  return /* @__PURE__ */ new Map();
@@ -12800,7 +13454,7 @@ async function parseZCodeDb(filePath, options) {
12800
13454
  for (let i = 0; i < 12; i++) {
12801
13455
  const probe = path22.join(candidate, ".zcode", "v2", "config.json");
12802
13456
  try {
12803
- await stat13(probe);
13457
+ await stat14(probe);
12804
13458
  configPath2 = probe;
12805
13459
  break;
12806
13460
  } catch {
@@ -13026,7 +13680,7 @@ async function zcodeBackfillFiles(sourceRoot, home, env) {
13026
13680
  const candidate = sourceRoot || zcodeDbPath(home, env);
13027
13681
  const filePath = candidate.endsWith(".sqlite") ? candidate : path22.join(candidate, "db", "db.sqlite");
13028
13682
  try {
13029
- const info = await stat13(filePath);
13683
+ const info = await stat14(filePath);
13030
13684
  return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
13031
13685
  } catch {
13032
13686
  return [];
@@ -13411,19 +14065,19 @@ async function parseZedSessionFile(dbPath, options) {
13411
14065
  return events.filter((event) => matchesBackfillFilters(event, options));
13412
14066
  }
13413
14067
  async function zedBackfillFiles(sourceRoot, home = os12.homedir(), env) {
13414
- const { stat: stat16 } = await import("node:fs/promises");
14068
+ const { stat: stat17 } = await import("node:fs/promises");
13415
14069
  if (sourceRoot) {
13416
14070
  if (!sourceRoot.endsWith(".db")) {
13417
14071
  return [];
13418
14072
  }
13419
- const info = await stat16(sourceRoot).catch(() => null);
14073
+ const info = await stat17(sourceRoot).catch(() => null);
13420
14074
  if (!info) {
13421
14075
  return [];
13422
14076
  }
13423
14077
  return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
13424
14078
  }
13425
14079
  for (const candidatePath of zedThreadsCandidates(home, env)) {
13426
- const info = await stat16(candidatePath).catch(() => null);
14080
+ const info = await stat17(candidatePath).catch(() => null);
13427
14081
  if (info) {
13428
14082
  return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
13429
14083
  }
@@ -13844,7 +14498,7 @@ async function uninstallEntry(entry, options) {
13844
14498
  await uninstallGeneratedFile(entry.path, options);
13845
14499
  }
13846
14500
  async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
13847
- const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
14501
+ const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
13848
14502
  const pathMod = await import("node:path");
13849
14503
  if (dryRun) {
13850
14504
  onWrite(`Would merge ${filePath}`);
@@ -13865,12 +14519,12 @@ async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
13865
14519
  onWrite(`Already installed ${filePath}`);
13866
14520
  return;
13867
14521
  }
13868
- await mkdir6(pathMod.dirname(filePath), { recursive: true });
13869
- await writeFile5(filePath, nextText, "utf8");
14522
+ await mkdir7(pathMod.dirname(filePath), { recursive: true });
14523
+ await writeFile6(filePath, nextText, "utf8");
13870
14524
  onWrite(`Installed ${filePath}`);
13871
14525
  }
13872
14526
  async function mergeCursorHooksFile(filePath, content, { dryRun, force, onWrite }) {
13873
- const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
14527
+ const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
13874
14528
  const pathMod = await import("node:path");
13875
14529
  if (dryRun) {
13876
14530
  onWrite(`Would merge ${filePath}`);
@@ -13890,8 +14544,8 @@ async function mergeCursorHooksFile(filePath, content, { dryRun, force, onWrite
13890
14544
  onWrite(`Already installed ${filePath}`);
13891
14545
  return;
13892
14546
  }
13893
- await mkdir6(pathMod.dirname(filePath), { recursive: true });
13894
- await writeFile5(filePath, nextText, "utf8");
14547
+ await mkdir7(pathMod.dirname(filePath), { recursive: true });
14548
+ await writeFile6(filePath, nextText, "utf8");
13895
14549
  onWrite(`Installed ${filePath}`);
13896
14550
  }
13897
14551
  async function uninstallCursorHooksFile(filePath, content, { dryRun, onWrite }) {
@@ -13925,8 +14579,8 @@ async function uninstallCursorHooksFile(filePath, content, { dryRun, onWrite })
13925
14579
  onWrite(`Would uninstall ${filePath}`);
13926
14580
  return;
13927
14581
  }
13928
- const { writeFile: writeFile5 } = await import("node:fs/promises");
13929
- await writeFile5(filePath, `${JSON.stringify(next, null, 2)}
14582
+ const { writeFile: writeFile6 } = await import("node:fs/promises");
14583
+ await writeFile6(filePath, `${JSON.stringify(next, null, 2)}
13930
14584
  `, "utf8");
13931
14585
  onWrite(`Uninstalled ${filePath}`);
13932
14586
  }
@@ -14001,7 +14655,7 @@ function hookCommandFromGroup(group) {
14001
14655
  return isPlainObject(hook) && typeof hook.command === "string" ? hook.command : void 0;
14002
14656
  }
14003
14657
  async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
14004
- const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
14658
+ const { mkdir: mkdir7, writeFile: writeFile6 } = await import("node:fs/promises");
14005
14659
  const pathMod = await import("node:path");
14006
14660
  if (dryRun) {
14007
14661
  onWrite(`Would merge ${filePath}`);
@@ -14029,8 +14683,8 @@ async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
14029
14683
  return;
14030
14684
  }
14031
14685
  }
14032
- await mkdir6(pathMod.dirname(filePath), { recursive: true });
14033
- await writeFile5(filePath, nextText, "utf8");
14686
+ await mkdir7(pathMod.dirname(filePath), { recursive: true });
14687
+ await writeFile6(filePath, nextText, "utf8");
14034
14688
  onWrite(`Installed ${filePath}`);
14035
14689
  }
14036
14690
  async function uninstallHooksToml(filePath, content, { dryRun, onWrite }) {
@@ -14055,8 +14709,8 @@ async function uninstallHooksToml(filePath, content, { dryRun, onWrite }) {
14055
14709
  onWrite(`Would uninstall ${filePath}`);
14056
14710
  return;
14057
14711
  }
14058
- const { writeFile: writeFile5 } = await import("node:fs/promises");
14059
- await writeFile5(filePath, nextText, "utf8");
14712
+ const { writeFile: writeFile6 } = await import("node:fs/promises");
14713
+ await writeFile6(filePath, nextText, "utf8");
14060
14714
  onWrite(`Uninstalled ${filePath}`);
14061
14715
  }
14062
14716
  function collectHookCommandsFromJsonContent(content) {
@@ -14189,8 +14843,8 @@ async function uninstallHooksJson(filePath, content, { dryRun, onWrite }) {
14189
14843
  onWrite(`Would uninstall ${filePath}`);
14190
14844
  return;
14191
14845
  }
14192
- const { writeFile: writeFile5 } = await import("node:fs/promises");
14193
- await writeFile5(filePath, `${JSON.stringify(next, null, 2)}
14846
+ const { writeFile: writeFile6 } = await import("node:fs/promises");
14847
+ await writeFile6(filePath, `${JSON.stringify(next, null, 2)}
14194
14848
  `, "utf8");
14195
14849
  onWrite(`Uninstalled ${filePath}`);
14196
14850
  }
@@ -14272,7 +14926,7 @@ function defaultMachineName() {
14272
14926
  init_fs();
14273
14927
 
14274
14928
  // src/lib/logger.ts
14275
- import { appendFile, mkdir as mkdir4, rename, stat as stat14 } from "node:fs/promises";
14929
+ import { appendFile, mkdir as mkdir5, rename, stat as stat15 } from "node:fs/promises";
14276
14930
  import { homedir as homedir2 } from "node:os";
14277
14931
  import path25 from "node:path";
14278
14932
  var MAX_BYTES = 1 * 1024 * 1024;
@@ -14290,7 +14944,7 @@ function serializeError(error) {
14290
14944
  }
14291
14945
  async function rotateIfNeeded(file) {
14292
14946
  try {
14293
- const info = await stat14(file);
14947
+ const info = await stat15(file);
14294
14948
  if (info.size > MAX_BYTES) {
14295
14949
  await rename(file, `${file}.1`).catch(() => {
14296
14950
  });
@@ -14301,7 +14955,7 @@ async function rotateIfNeeded(file) {
14301
14955
  async function writeLog(entry, home = homedir2(), fileName = "cli.log") {
14302
14956
  try {
14303
14957
  const dir = logDir(home);
14304
- await mkdir4(dir, { recursive: true });
14958
+ await mkdir5(dir, { recursive: true });
14305
14959
  const file = logPath(home, fileName);
14306
14960
  await rotateIfNeeded(file);
14307
14961
  const record = {
@@ -14526,7 +15180,7 @@ async function deleteMachine(remote, id) {
14526
15180
  }
14527
15181
 
14528
15182
  // src/lib/types.ts
14529
- var BACKFILL_STATE_SCHEMA_VERSION = 7;
15183
+ var BACKFILL_STATE_SCHEMA_VERSION = 9;
14530
15184
 
14531
15185
  // src/cli.ts
14532
15186
  function createRegistry() {
@@ -15105,7 +15759,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
15105
15759
  return zedBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
15106
15760
  }
15107
15761
  if (source.id === "cursor") {
15108
- return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
15762
+ return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env, options);
15109
15763
  }
15110
15764
  if (source.id === "pi") {
15111
15765
  return piBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
@@ -15114,7 +15768,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
15114
15768
  const fileLists = await Promise.all(roots.map((r) => listJsonlFiles(r)));
15115
15769
  const files = fileLists.flat().sort().slice(0, numberOption(options.limit) || void 0);
15116
15770
  return Promise.all(files.map(async (filePath) => {
15117
- const info = await stat15(filePath);
15771
+ const info = await stat16(filePath);
15118
15772
  return { path: filePath, modifiedAt: info.mtime.toISOString() };
15119
15773
  }));
15120
15774
  }
@@ -15514,8 +16168,8 @@ async function readBackfillIncrementalStateFile(home, ctx) {
15514
16168
  }
15515
16169
  async function writeBackfillIncrementalStateFile(home, file) {
15516
16170
  const statePath = backfillIncrementalStatePath(home);
15517
- await mkdir5(path26.dirname(statePath), { recursive: true });
15518
- await writeFile4(statePath, `${JSON.stringify(file, null, 2)}
16171
+ await mkdir6(path26.dirname(statePath), { recursive: true });
16172
+ await writeFile5(statePath, `${JSON.stringify(file, null, 2)}
15519
16173
  `, "utf8");
15520
16174
  }
15521
16175
  async function readBackfillIncrementalState(home, remoteKey, ctx) {
@@ -15563,8 +16217,8 @@ async function readSyncLocalTriggerState(statePath) {
15563
16217
  return nextState;
15564
16218
  }
15565
16219
  async function writeSyncLocalTriggerState(statePath, state) {
15566
- await mkdir5(path26.dirname(statePath), { recursive: true });
15567
- await writeFile4(statePath, `${JSON.stringify(state, null, 2)}
16220
+ await mkdir6(path26.dirname(statePath), { recursive: true });
16221
+ await writeFile5(statePath, `${JSON.stringify(state, null, 2)}
15568
16222
  `, "utf8");
15569
16223
  }
15570
16224
  async function readSyncLocalLock(lockPath) {
@@ -15578,12 +16232,12 @@ async function readSyncLocalLock(lockPath) {
15578
16232
  return { pid: lock.pid, startedAt: lock.startedAt };
15579
16233
  }
15580
16234
  async function writeSyncLocalLock(lockPath, lock) {
15581
- await mkdir5(path26.dirname(lockPath), { recursive: true });
15582
- await writeFile4(lockPath, `${JSON.stringify(lock, null, 2)}
16235
+ await mkdir6(path26.dirname(lockPath), { recursive: true });
16236
+ await writeFile5(lockPath, `${JSON.stringify(lock, null, 2)}
15583
16237
  `, "utf8");
15584
16238
  }
15585
16239
  async function acquireSyncLocalLock(lockPath, lock) {
15586
- await mkdir5(path26.dirname(lockPath), { recursive: true });
16240
+ await mkdir6(path26.dirname(lockPath), { recursive: true });
15587
16241
  try {
15588
16242
  const handle = await open(lockPath, "wx");
15589
16243
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.57",
4
+ "version": "0.1.59",
5
5
  "description": "vibetime CLI — install AI-agent hooks (Claude Code, Codex, OpenCode, Pi, Cursor) and report activity to vibetime.",
6
6
  "license": "MIT",
7
7
  "publishConfig": {