@yhong91/vibetime 0.1.74 → 0.1.75

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 +155 -146
  2. package/package.json +1 -1
package/bin/vibetime.mjs CHANGED
@@ -884,8 +884,8 @@ 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 mkdir6, open, rm, stat as stat18, writeFile as writeFile5 } from "node:fs/promises";
888
- import os14 from "node:os";
887
+ import { mkdir as mkdir6, open, rm, stat as stat17, writeFile as writeFile5 } from "node:fs/promises";
888
+ import os13 from "node:os";
889
889
  import path27 from "node:path";
890
890
  import { fileURLToPath } from "node:url";
891
891
 
@@ -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.74" : "0.1.1";
2050
+ var PACKAGE_VERSION = true ? "0.1.75" : "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;
@@ -2103,6 +2103,9 @@ function msToIso(ms) {
2103
2103
  }
2104
2104
 
2105
2105
  // src/lib/backfill.ts
2106
+ function rollupKeyForSourceSession(source, sessionId) {
2107
+ return createImportKey(["rollup", source, sessionId]);
2108
+ }
2106
2109
  function withBackfillRefs(event, context) {
2107
2110
  const importKey = createImportKey([
2108
2111
  event.source,
@@ -4936,8 +4939,8 @@ async function codebuddyBackfillFiles(sourceRoot, home, env) {
4936
4939
  }
4937
4940
  const filePath = path9.join(traceDir, entry);
4938
4941
  try {
4939
- const stat19 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
4940
- files.push({ path: filePath, modifiedAt: stat19.mtime.toISOString(), groupId: pidDir.name });
4942
+ const stat18 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
4943
+ files.push({ path: filePath, modifiedAt: stat18.mtime.toISOString(), groupId: pidDir.name });
4941
4944
  } catch {
4942
4945
  }
4943
4946
  }
@@ -6500,10 +6503,10 @@ function cloudAgentSessionFromEvents(conversationId, events) {
6500
6503
  };
6501
6504
  }
6502
6505
  function hookUncachedInputTokens(turn) {
6503
- return Math.max(0, (turn.tokensInput || 0) - (turn.tokensCacheReadInput || 0));
6506
+ return Math.max(0, (turn.tokensInput || 0) - (turn.tokensCacheReadInput || 0) - (turn.tokensCacheCreationInput || 0));
6504
6507
  }
6505
6508
  function cloudEventMatchesHookTurn(event, turn) {
6506
- return event.tokensInput === hookUncachedInputTokens(turn) && event.tokensOutput === (turn.tokensOutput || 0) && event.tokensCacheReadInput === (turn.tokensCacheReadInput || 0);
6509
+ return event.tokensInput === hookUncachedInputTokens(turn) && event.tokensOutput === (turn.tokensOutput || 0) && event.tokensCacheReadInput === (turn.tokensCacheReadInput || 0) && event.tokensCacheCreationInput === (turn.tokensCacheCreationInput || 0);
6507
6510
  }
6508
6511
  function unmatchedCloudEvents(events, turns) {
6509
6512
  const used = /* @__PURE__ */ new Set();
@@ -6846,6 +6849,11 @@ async function loadCursorCloudUsageMap(options, filePath) {
6846
6849
  return cached;
6847
6850
  }
6848
6851
  const home = stringOption(options.home) || os6.homedir();
6852
+ if (options.skipCursorCloud) {
6853
+ return cloudUsageMapFromSessions(
6854
+ parseCursorCloudUsageCache(await readJsonIfExists(cursorCloudUsageCachePath(home)))
6855
+ );
6856
+ }
6849
6857
  const intervalSeconds = cursorCloudFetchIntervalSeconds();
6850
6858
  if (intervalSeconds > 0 && !options.force) {
6851
6859
  const lastFetchedMs = await readLastCloudFetchAt(home);
@@ -6967,7 +6975,7 @@ async function writeCursorCloudUsageCache(cachePath, sessions) {
6967
6975
  const info = await stat8(cachePath);
6968
6976
  return info.mtime.toISOString();
6969
6977
  }
6970
- function parseCursorCloudAgentSessions(filePath, options, sessions, localIds, identity) {
6978
+ function parseCursorCloudAgentSessions(filePath, options, sessions, localIds) {
6971
6979
  const events = [];
6972
6980
  const sourcePathHash = `sha256:${createStableHash(filePath)}`;
6973
6981
  const project = CURSOR_CLOUD_AGENT_PROJECT;
@@ -6984,8 +6992,8 @@ function parseCursorCloudAgentSessions(filePath, options, sessions, localIds, id
6984
6992
  lineNumber += 1;
6985
6993
  const event = {
6986
6994
  schemaVersion: AGENT_TIME_SCHEMA_VERSION,
6987
- source: identity.source,
6988
- agent: identity.agent,
6995
+ source: SOURCE_ID,
6996
+ agent: AGENT_NAME,
6989
6997
  workspaceId,
6990
6998
  project,
6991
6999
  model: lastModel,
@@ -7042,7 +7050,7 @@ function parseCursorCloudAgentSessions(filePath, options, sessions, localIds, id
7042
7050
  }
7043
7051
  return events;
7044
7052
  }
7045
- async function parseCursorCloudAgentFile(filePath, options, identity) {
7053
+ async function parseCursorCloudAgentFile(filePath, options) {
7046
7054
  const home = stringOption(options.home) || os6.homedir();
7047
7055
  const env = {
7048
7056
  CURSOR_HOME: process.env.CURSOR_HOME,
@@ -7054,7 +7062,7 @@ async function parseCursorCloudAgentFile(filePath, options, identity) {
7054
7062
  return session ? [session] : [];
7055
7063
  }) : parseCursorCloudUsageCache(await readJsonIfExists(filePath));
7056
7064
  const localIds = await listLocalCursorSessionIds(home, env);
7057
- return parseCursorCloudAgentSessions(filePath, options, sessions, localIds, identity);
7065
+ return parseCursorCloudAgentSessions(filePath, options, sessions, localIds);
7058
7066
  }
7059
7067
  function decodeKv(value) {
7060
7068
  if (typeof value === "string") {
@@ -7913,6 +7921,9 @@ async function parseCursorSessionFile(filePath, options) {
7913
7921
  if (base.endsWith(".jsonl")) {
7914
7922
  return parseCursorTranscriptFile(filePath, options);
7915
7923
  }
7924
+ if (base === CURSOR_CLOUD_USAGE_FILENAME) {
7925
+ return parseCursorCloudAgentFile(filePath, options);
7926
+ }
7916
7927
  if (base === "store.db") {
7917
7928
  return [];
7918
7929
  }
@@ -8147,7 +8158,36 @@ function parseComposer(db, composer, filePath, sourcePathHash, options, persiste
8147
8158
  emitPersistedCursorUsage(push, persistedUsage, endedAt || startedAt, currentTurnId, model, sessionId, events.some((event) => event.type === "model.usage"));
8148
8159
  return events;
8149
8160
  }
8150
- async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
8161
+ async function cursorCloudUsageBackfillFiles(home, options) {
8162
+ if (options?.skipCursorCloud || cursorCloudUsageDisabled()) {
8163
+ return [];
8164
+ }
8165
+ const cachePath = cursorCloudUsageCachePath(home);
8166
+ if (!options) {
8167
+ const info = await stat8(cachePath).catch(() => null);
8168
+ return info ? [{ path: cachePath, modifiedAt: info.mtime.toISOString() }] : [];
8169
+ }
8170
+ const map = await loadCursorCloudUsageMap(options);
8171
+ if (map.size === 0) {
8172
+ const info = await stat8(cachePath).catch(() => null);
8173
+ return info ? [{ path: cachePath, modifiedAt: info.mtime.toISOString() }] : [];
8174
+ }
8175
+ const sessions = [];
8176
+ for (const [conversationId, events] of map) {
8177
+ const session = cloudAgentSessionFromEvents(conversationId, events);
8178
+ if (session) {
8179
+ sessions.push(session);
8180
+ }
8181
+ }
8182
+ if (sessions.length === 0) {
8183
+ return [];
8184
+ }
8185
+ return [{
8186
+ path: cachePath,
8187
+ modifiedAt: await writeCursorCloudUsageCache(cachePath, sessions)
8188
+ }];
8189
+ }
8190
+ async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env, options) {
8151
8191
  if (sourceRoot) {
8152
8192
  const info = await stat8(sourceRoot).catch(() => null);
8153
8193
  if (!info) {
@@ -8177,6 +8217,9 @@ async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
8177
8217
  files2.push(...await collectCursorTranscriptFiles(sourceRoot, home, skipIds2));
8178
8218
  return files2;
8179
8219
  }
8220
+ if (path13.basename(sourceRoot) === CURSOR_CLOUD_USAGE_FILENAME) {
8221
+ return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
8222
+ }
8180
8223
  if (path13.basename(sourceRoot) === "store.db") {
8181
8224
  return [];
8182
8225
  }
@@ -8214,6 +8257,7 @@ async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
8214
8257
  break;
8215
8258
  }
8216
8259
  files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), home, skipIds));
8260
+ files.push(...await cursorCloudUsageBackfillFiles(home, options));
8217
8261
  return files;
8218
8262
  }
8219
8263
  function cursorHookConfig() {
@@ -8251,85 +8295,22 @@ function createCursorAdapter() {
8251
8295
  return [
8252
8296
  ...cursorStateDbCandidates(home, env),
8253
8297
  cursorProjectsDir(home, env),
8254
- cursorChatsDir(home, env)
8298
+ cursorChatsDir(home, env),
8299
+ cursorCloudUsageCachePath(home)
8255
8300
  ];
8256
8301
  },
8257
8302
  parseSessionFile: parseCursorSessionFile
8258
8303
  };
8259
8304
  }
8260
8305
 
8261
- // src/adapters/grok-bot.ts
8262
- import { stat as stat9 } from "node:fs/promises";
8263
- import os7 from "node:os";
8264
- var SOURCE_ID2 = "grok-bot";
8265
- var AGENT_NAME2 = "grok-bot";
8266
- var IDENTITY = { source: SOURCE_ID2, agent: AGENT_NAME2 };
8267
- async function grokBotBackfillFiles(sourceRoot, home = os7.homedir(), _env, options) {
8268
- if (sourceRoot) {
8269
- const info = await stat9(sourceRoot).catch(() => null);
8270
- return info ? [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }] : [];
8271
- }
8272
- if (!options) {
8273
- return [];
8274
- }
8275
- const cachePath = cursorCloudUsageCachePath(home);
8276
- const map = await loadCursorCloudUsageMap(options);
8277
- if (map.size === 0) {
8278
- const info = await stat9(cachePath).catch(() => null);
8279
- return info ? [{ path: cachePath, modifiedAt: info.mtime.toISOString() }] : [];
8280
- }
8281
- const sessions = [];
8282
- for (const [conversationId, events] of map) {
8283
- const session = cloudAgentSessionFromEvents(conversationId, events);
8284
- if (session) {
8285
- sessions.push(session);
8286
- }
8287
- }
8288
- if (sessions.length === 0) {
8289
- return [];
8290
- }
8291
- return [{
8292
- path: cachePath,
8293
- modifiedAt: await writeCursorCloudUsageCache(cachePath, sessions)
8294
- }];
8295
- }
8296
- function createGrokBotAdapter() {
8297
- return {
8298
- id: SOURCE_ID2,
8299
- label: "Grok Bot",
8300
- agentName: AGENT_NAME2,
8301
- kind: "agent",
8302
- // No hooks to install — data arrives via the Cursor dashboard API.
8303
- // The cache file stands in as the detect/install marker.
8304
- detectPath(home) {
8305
- return cursorCloudUsageCachePath(home);
8306
- },
8307
- installedPath(home) {
8308
- return cursorCloudUsageCachePath(home);
8309
- },
8310
- async isInstalled() {
8311
- return false;
8312
- },
8313
- installEntries() {
8314
- return [];
8315
- },
8316
- sourcePaths(home) {
8317
- return [cursorCloudUsageCachePath(home)];
8318
- },
8319
- parseSessionFile(filePath, options) {
8320
- return parseCursorCloudAgentFile(filePath, options, IDENTITY);
8321
- }
8322
- };
8323
- }
8324
-
8325
8306
  // src/adapters/grok-build.ts
8326
- import { readdir as readdir7, readFile as readFile9, stat as stat10 } from "node:fs/promises";
8307
+ import { readdir as readdir7, readFile as readFile9, stat as stat9 } from "node:fs/promises";
8327
8308
  import path14 from "node:path";
8328
8309
  init_fs();
8329
8310
  var GROK_COST_TICKS_PER_USD = 1e9;
8330
- var SOURCE_ID3 = "grok-build";
8331
- var AGENT_NAME3 = "grok-build";
8332
- var HOOK_COMMAND2 = `vibetime hook --agent ${SOURCE_ID3}`;
8311
+ var SOURCE_ID2 = "grok-build";
8312
+ var AGENT_NAME2 = "grok-build";
8313
+ var HOOK_COMMAND2 = `vibetime hook --agent ${SOURCE_ID2}`;
8333
8314
  function grokHome(home, env) {
8334
8315
  const override = env?.GROK_HOME;
8335
8316
  if (override && override.trim()) {
@@ -8363,7 +8344,7 @@ async function grokBackfillFiles(sourceRoot, home, env) {
8363
8344
  continue;
8364
8345
  }
8365
8346
  try {
8366
- const info = await stat10(entryPath);
8347
+ const info = await stat9(entryPath);
8367
8348
  files.push({ path: entryPath, modifiedAt: info.mtime.toISOString() });
8368
8349
  } catch {
8369
8350
  }
@@ -8415,8 +8396,8 @@ async function parseGrokSessionFile(filePath, options) {
8415
8396
  };
8416
8397
  const base = (partial) => ({
8417
8398
  schemaVersion: AGENT_TIME_SCHEMA_VERSION,
8418
- source: SOURCE_ID3,
8419
- agent: AGENT_NAME3,
8399
+ source: SOURCE_ID2,
8400
+ agent: AGENT_NAME2,
8420
8401
  workspaceId,
8421
8402
  project,
8422
8403
  cwd,
@@ -8976,7 +8957,7 @@ async function loadHunkFileActivities(hunkPath, cwd) {
8976
8957
  }
8977
8958
  return activities;
8978
8959
  }
8979
- var grokHandler = (msg) => hookHandler(SOURCE_ID3, msg);
8960
+ var grokHandler = (msg) => hookHandler(SOURCE_ID2, msg);
8980
8961
  function hookConfig5() {
8981
8962
  const anyTool = [{ matcher: ".*", hooks: [grokHandler("Reporting tool activity")] }];
8982
8963
  return {
@@ -8998,9 +8979,9 @@ function hookConfig5() {
8998
8979
  }
8999
8980
  function createGrokBuildAdapter() {
9000
8981
  return {
9001
- id: SOURCE_ID3,
8982
+ id: SOURCE_ID2,
9002
8983
  label: "Grok Build",
9003
- agentName: AGENT_NAME3,
8984
+ agentName: AGENT_NAME2,
9004
8985
  kind: "agent",
9005
8986
  detectPath(home, env) {
9006
8987
  return grokHome(home, env);
@@ -9026,7 +9007,7 @@ function createGrokBuildAdapter() {
9026
9007
  }
9027
9008
 
9028
9009
  // src/adapters/hermes.ts
9029
- import { readdir as readdir8, stat as stat11 } from "node:fs/promises";
9010
+ import { readdir as readdir8, stat as stat10 } from "node:fs/promises";
9030
9011
  import path15 from "node:path";
9031
9012
  init_fs();
9032
9013
  var SOURCE = "hermes";
@@ -9319,11 +9300,11 @@ function hermesToolAlias(tool) {
9319
9300
  }
9320
9301
  async function sqliteMtime(filePath) {
9321
9302
  try {
9322
- const info = await stat11(filePath);
9303
+ const info = await stat10(filePath);
9323
9304
  let modifiedMs = info.mtimeMs;
9324
9305
  for (const suffix of ["-wal", "-shm"]) {
9325
9306
  try {
9326
- const sidecar = await stat11(`${filePath}${suffix}`);
9307
+ const sidecar = await stat10(`${filePath}${suffix}`);
9327
9308
  modifiedMs = Math.max(modifiedMs, sidecar.mtimeMs);
9328
9309
  } catch {
9329
9310
  }
@@ -10616,7 +10597,7 @@ function createKimiCodeAdapter() {
10616
10597
  }
10617
10598
 
10618
10599
  // src/adapters/opencode.ts
10619
- import os8 from "node:os";
10600
+ import os7 from "node:os";
10620
10601
  import path17 from "node:path";
10621
10602
  async function parseOpenCodeSessionFile(dbPath, options) {
10622
10603
  const { DatabaseSync } = await import("node:sqlite");
@@ -11071,20 +11052,20 @@ function opencodeDataCandidates(home, env) {
11071
11052
  const primary = xdgData && xdgData.trim() ? path17.join(path17.resolve(xdgData), "opencode", "opencode.db") : path17.join(home, ".local", "share", "opencode", "opencode.db");
11072
11053
  return [primary, path17.join(home, ".opencode", "opencode.db")];
11073
11054
  }
11074
- async function opencodeBackfillFiles(sourceRoot, home = os8.homedir(), env) {
11075
- const { stat: stat19 } = await import("node:fs/promises");
11055
+ async function opencodeBackfillFiles(sourceRoot, home = os7.homedir(), env) {
11056
+ const { stat: stat18 } = await import("node:fs/promises");
11076
11057
  if (sourceRoot) {
11077
11058
  if (!sourceRoot.endsWith(".db")) {
11078
11059
  return [];
11079
11060
  }
11080
- const info = await stat19(sourceRoot).catch(() => null);
11061
+ const info = await stat18(sourceRoot).catch(() => null);
11081
11062
  if (!info) {
11082
11063
  return [];
11083
11064
  }
11084
11065
  return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
11085
11066
  }
11086
11067
  for (const candidatePath of opencodeDataCandidates(home, env)) {
11087
- const info = await stat19(candidatePath).catch(() => null);
11068
+ const info = await stat18(candidatePath).catch(() => null);
11088
11069
  if (info) {
11089
11070
  return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
11090
11071
  }
@@ -11184,8 +11165,8 @@ function createOpenCodeAdapter() {
11184
11165
  }
11185
11166
 
11186
11167
  // src/adapters/pi.ts
11187
- import { readdir as readdir9, readFile as readFile11, stat as stat12 } from "node:fs/promises";
11188
- import os9 from "node:os";
11168
+ import { readdir as readdir9, readFile as readFile11, stat as stat11 } from "node:fs/promises";
11169
+ import os8 from "node:os";
11189
11170
  import path18 from "node:path";
11190
11171
  init_fs();
11191
11172
  function piHostSessionIdFromBasename(basename) {
@@ -11596,7 +11577,7 @@ async function parsePiWorkflowRunFile(filePath, options) {
11596
11577
  return state.events.filter((event) => matchesBackfillFilters(event, options));
11597
11578
  }
11598
11579
  async function findPiSessionFileById(sessionId, options) {
11599
- const sessionsDir = piSessionDir(path18.resolve(stringOption(options.home) || os9.homedir()));
11580
+ const sessionsDir = piSessionDir(path18.resolve(stringOption(options.home) || os8.homedir()));
11600
11581
  try {
11601
11582
  const projectDirs = await readdir9(sessionsDir, { withFileTypes: true });
11602
11583
  for (const dir of projectDirs) {
@@ -11873,14 +11854,14 @@ async function workflowRunGroupId(filePath) {
11873
11854
  return void 0;
11874
11855
  }
11875
11856
  }
11876
- async function piBackfillFiles(sourceRoot, home = os9.homedir(), env) {
11857
+ async function piBackfillFiles(sourceRoot, home = os8.homedir(), env) {
11877
11858
  const lists = sourceRoot ? [await listFilesByExtensions(sourceRoot, [".jsonl", ".json"])] : await Promise.all([
11878
11859
  listFilesByExtensions(piSessionDir(home, env), [".jsonl"]),
11879
11860
  listFilesByExtensions(piWorkflowProjectsDir(home), [".json"])
11880
11861
  ]);
11881
11862
  const files = lists.flat().sort();
11882
11863
  return Promise.all(files.map(async (filePath) => {
11883
- const info = await stat12(filePath);
11864
+ const info = await stat11(filePath);
11884
11865
  let groupId = resolvePiBackfillGroupId(filePath);
11885
11866
  if (!groupId && filePath.endsWith(".json")) {
11886
11867
  groupId = await workflowRunGroupId(filePath);
@@ -11927,13 +11908,13 @@ function createPiAdapter() {
11927
11908
  }
11928
11909
 
11929
11910
  // src/adapters/qoder-cn.ts
11930
- import { readdir as readdir10, readFile as readFile12, stat as stat13 } from "node:fs/promises";
11931
- import os11 from "node:os";
11911
+ import { readdir as readdir10, readFile as readFile12, stat as stat12 } from "node:fs/promises";
11912
+ import os10 from "node:os";
11932
11913
  import path20 from "node:path";
11933
11914
 
11934
11915
  // src/adapters/qoder-local-db.ts
11935
11916
  import { access } from "node:fs/promises";
11936
- import os10 from "node:os";
11917
+ import os9 from "node:os";
11937
11918
  import path19 from "node:path";
11938
11919
  function takeQoderDbModelCall(calls, requestId, blockStart) {
11939
11920
  if (requestId) {
@@ -11951,7 +11932,7 @@ function takeQoderDbModelCall(calls, requestId, blockStart) {
11951
11932
  }
11952
11933
  return calls.ordered.shift();
11953
11934
  }
11954
- function appDataRoot(appDirName, home = os10.homedir()) {
11935
+ function appDataRoot(appDirName, home = os9.homedir()) {
11955
11936
  if (process.platform === "darwin") {
11956
11937
  return path19.join(home, "Library", "Application Support", appDirName);
11957
11938
  }
@@ -12260,7 +12241,7 @@ async function parseQoderCnSessionFile(filePath, options) {
12260
12241
  let cwd;
12261
12242
  let project = projectContext.project;
12262
12243
  let model;
12263
- const home = path20.resolve(stringOption(options.home) || os11.homedir());
12244
+ const home = path20.resolve(stringOption(options.home) || os10.homedir());
12264
12245
  const modelMap = await loadQoderCnModelNames(configDir2, home);
12265
12246
  const isSubagentSession = filePath.includes("subagents");
12266
12247
  const segmentModelCalls = await loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap);
@@ -12830,7 +12811,7 @@ async function gitRootFromCwds2(cwds) {
12830
12811
  while (!seen.has(current)) {
12831
12812
  seen.add(current);
12832
12813
  try {
12833
- await stat13(path20.join(current, ".git"));
12814
+ await stat12(path20.join(current, ".git"));
12834
12815
  return current;
12835
12816
  } catch {
12836
12817
  }
@@ -12864,7 +12845,7 @@ function encodeQoderCnProjectPath(value) {
12864
12845
  }
12865
12846
  async function qoderCnProjectFromFilePath(filePath, options) {
12866
12847
  const projectDir = path20.basename(path20.dirname(filePath));
12867
- const home = options ? path20.resolve(stringOption(options.home) || os11.homedir()) : os11.homedir();
12848
+ const home = options ? path20.resolve(stringOption(options.home) || os10.homedir()) : os10.homedir();
12868
12849
  const resolved = await resolveQoderCnProjectPath(projectDir, home);
12869
12850
  if (resolved) {
12870
12851
  return path20.basename(resolved);
@@ -12980,8 +12961,8 @@ function createQoderCnAdapter() {
12980
12961
 
12981
12962
  // src/adapters/qoder.ts
12982
12963
  import { existsSync } from "node:fs";
12983
- import { readdir as readdir11, readFile as readFile13, stat as stat14 } from "node:fs/promises";
12984
- import os12 from "node:os";
12964
+ import { readdir as readdir11, readFile as readFile13, stat as stat13 } from "node:fs/promises";
12965
+ import os11 from "node:os";
12985
12966
  import path21 from "node:path";
12986
12967
  function parseQoderPaths(filePath) {
12987
12968
  const parts = filePath.split(path21.sep);
@@ -13146,7 +13127,7 @@ async function parseQoderSessionFile(filePath, options) {
13146
13127
  let cwd;
13147
13128
  let project = projectContext.project;
13148
13129
  let model;
13149
- const home = path21.resolve(stringOption(options.home) || os12.homedir());
13130
+ const home = path21.resolve(stringOption(options.home) || os11.homedir());
13150
13131
  const modelMap = await loadQoderModelNames(configDir2, home);
13151
13132
  const qwenworkRoot = isQwenworkConfigRoot(configDir2);
13152
13133
  const isSubagentSession = filePath.includes("subagents");
@@ -13682,7 +13663,7 @@ async function gitRootFromCwds3(cwds) {
13682
13663
  while (!seen.has(current)) {
13683
13664
  seen.add(current);
13684
13665
  try {
13685
- await stat14(path21.join(current, ".git"));
13666
+ await stat13(path21.join(current, ".git"));
13686
13667
  return current;
13687
13668
  } catch {
13688
13669
  }
@@ -13734,7 +13715,7 @@ function qoderEncodedProjectSuffix(projectDir, home) {
13734
13715
  }
13735
13716
  async function qoderProjectFromFilePath(filePath, options) {
13736
13717
  const projectDir = path21.basename(path21.dirname(filePath));
13737
- const home = options ? path21.resolve(stringOption(options.home) || os12.homedir()) : os12.homedir();
13718
+ const home = options ? path21.resolve(stringOption(options.home) || os11.homedir()) : os11.homedir();
13738
13719
  const resolved = await resolveQoderProjectPath(projectDir, home);
13739
13720
  if (resolved) {
13740
13721
  return path21.basename(resolved);
@@ -13886,11 +13867,14 @@ function normalizeId(id) {
13886
13867
  if (id === "claude") {
13887
13868
  return "claude-code";
13888
13869
  }
13870
+ if (id === "grok-bot") {
13871
+ return "cursor";
13872
+ }
13889
13873
  return id;
13890
13874
  }
13891
13875
 
13892
13876
  // src/adapters/workbuddy.ts
13893
- import { readdir as readdir12, readFile as readFile14, stat as stat15 } from "node:fs/promises";
13877
+ import { readdir as readdir12, readFile as readFile14, stat as stat14 } from "node:fs/promises";
13894
13878
  import path22 from "node:path";
13895
13879
  function workbuddyProjectsDir(home, env) {
13896
13880
  const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
@@ -14343,7 +14327,7 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
14343
14327
  for (const entry of entries) {
14344
14328
  if (entry.isFile() && entry.name.endsWith(".jsonl")) {
14345
14329
  const filePath = path22.join(projectDir, entry.name);
14346
- const info = await stat15(filePath);
14330
+ const info = await stat14(filePath);
14347
14331
  files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
14348
14332
  }
14349
14333
  }
@@ -14404,7 +14388,7 @@ function createWorkbuddyAdapter() {
14404
14388
 
14405
14389
  // src/adapters/zcode.ts
14406
14390
  import { execFile } from "node:child_process";
14407
- import { readFile as readFile15, stat as stat16 } from "node:fs/promises";
14391
+ import { readFile as readFile15, stat as stat15 } from "node:fs/promises";
14408
14392
  import path23 from "node:path";
14409
14393
  import { promisify as promisify2 } from "node:util";
14410
14394
  var execFileAsync = promisify2(execFile);
@@ -14443,7 +14427,7 @@ var providerNameCache = null;
14443
14427
  async function loadProviderNames(configPath2) {
14444
14428
  let fileMtime = 0;
14445
14429
  try {
14446
- const info = await stat16(configPath2);
14430
+ const info = await stat15(configPath2);
14447
14431
  fileMtime = info.mtimeMs;
14448
14432
  } catch {
14449
14433
  return /* @__PURE__ */ new Map();
@@ -14671,7 +14655,7 @@ async function parseZCodeDb(filePath, options) {
14671
14655
  for (let i = 0; i < 12; i++) {
14672
14656
  const probe = path23.join(candidate, ".zcode", "v2", "config.json");
14673
14657
  try {
14674
- await stat16(probe);
14658
+ await stat15(probe);
14675
14659
  configPath2 = probe;
14676
14660
  break;
14677
14661
  } catch {
@@ -14905,11 +14889,11 @@ async function zcodeBackfillFiles(sourceRoot, home, env) {
14905
14889
  const candidate = sourceRoot || zcodeDbPath(home, env);
14906
14890
  const filePath = candidate.endsWith(".sqlite") ? candidate : path23.join(candidate, "db", "db.sqlite");
14907
14891
  try {
14908
- const info = await stat16(filePath);
14892
+ const info = await stat15(filePath);
14909
14893
  let modifiedMs = info.mtimeMs;
14910
14894
  for (const suffix of ["-wal", "-shm"]) {
14911
14895
  try {
14912
- const sidecar = await stat16(`${filePath}${suffix}`);
14896
+ const sidecar = await stat15(`${filePath}${suffix}`);
14913
14897
  modifiedMs = Math.max(modifiedMs, sidecar.mtimeMs);
14914
14898
  } catch {
14915
14899
  }
@@ -14952,7 +14936,7 @@ function createZCodeAdapter() {
14952
14936
  }
14953
14937
 
14954
14938
  // src/adapters/zed.ts
14955
- import os13 from "node:os";
14939
+ import os12 from "node:os";
14956
14940
  import path24 from "node:path";
14957
14941
  function zedThreadsCandidates(home, env) {
14958
14942
  const candidates = [];
@@ -15304,20 +15288,20 @@ async function parseZedSessionFile(dbPath, options) {
15304
15288
  }
15305
15289
  return events.filter((event) => matchesBackfillFilters(event, options));
15306
15290
  }
15307
- async function zedBackfillFiles(sourceRoot, home = os13.homedir(), env) {
15308
- const { stat: stat19 } = await import("node:fs/promises");
15291
+ async function zedBackfillFiles(sourceRoot, home = os12.homedir(), env) {
15292
+ const { stat: stat18 } = await import("node:fs/promises");
15309
15293
  if (sourceRoot) {
15310
15294
  if (!sourceRoot.endsWith(".db")) {
15311
15295
  return [];
15312
15296
  }
15313
- const info = await stat19(sourceRoot).catch(() => null);
15297
+ const info = await stat18(sourceRoot).catch(() => null);
15314
15298
  if (!info) {
15315
15299
  return [];
15316
15300
  }
15317
15301
  return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
15318
15302
  }
15319
15303
  for (const candidatePath of zedThreadsCandidates(home, env)) {
15320
- const info = await stat19(candidatePath).catch(() => null);
15304
+ const info = await stat18(candidatePath).catch(() => null);
15321
15305
  if (info) {
15322
15306
  return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
15323
15307
  }
@@ -16208,7 +16192,7 @@ function defaultMachineName() {
16208
16192
  init_fs();
16209
16193
 
16210
16194
  // src/lib/logger.ts
16211
- import { appendFile, mkdir as mkdir5, rename as rename2, stat as stat17 } from "node:fs/promises";
16195
+ import { appendFile, mkdir as mkdir5, rename as rename2, stat as stat16 } from "node:fs/promises";
16212
16196
  import { homedir as homedir2 } from "node:os";
16213
16197
  import path26 from "node:path";
16214
16198
  var MAX_BYTES = 1 * 1024 * 1024;
@@ -16226,7 +16210,7 @@ function serializeError(error) {
16226
16210
  }
16227
16211
  async function rotateIfNeeded(file) {
16228
16212
  try {
16229
- const info = await stat17(file);
16213
+ const info = await stat16(file);
16230
16214
  if (info.size > MAX_BYTES) {
16231
16215
  await rename2(file, `${file}.1`).catch(() => {
16232
16216
  });
@@ -16462,7 +16446,7 @@ async function deleteMachine(remote, id) {
16462
16446
  }
16463
16447
 
16464
16448
  // src/lib/types.ts
16465
- var BACKFILL_STATE_SCHEMA_VERSION = 10;
16449
+ var BACKFILL_STATE_SCHEMA_VERSION = 11;
16466
16450
 
16467
16451
  // src/cli.ts
16468
16452
  function createRegistry() {
@@ -16483,7 +16467,6 @@ function createRegistry() {
16483
16467
  registry.register(createZedAdapter());
16484
16468
  registry.register(createKimiCodeAdapter());
16485
16469
  registry.register(createCursorAdapter());
16486
- registry.register(createGrokBotAdapter());
16487
16470
  registry.register(createHermesAdapter());
16488
16471
  return registry;
16489
16472
  }
@@ -17044,10 +17027,14 @@ async function listBackfillSourceFiles(source, options, ctx) {
17044
17027
  return zedBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
17045
17028
  }
17046
17029
  if (source.id === "cursor") {
17047
- return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
17048
- }
17049
- if (source.id === "grok-bot") {
17050
- return grokBotBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env, options);
17030
+ const requested = normalizeBackfillSource(stringOption(options.source) || "all");
17031
+ const skipCursorCloud = requested === "all" && disabledBackfillSourceSet(readConfig(resolveHome3(options, ctx))).has("grok-bot");
17032
+ return cursorBackfillFiles(
17033
+ stringOption(options["source-root"]),
17034
+ resolveHome3(options, ctx),
17035
+ ctx.env,
17036
+ { ...options, skipCursorCloud }
17037
+ );
17051
17038
  }
17052
17039
  if (source.id === "hermes") {
17053
17040
  return hermesBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
@@ -17059,7 +17046,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
17059
17046
  const fileLists = await Promise.all(roots.map((r) => listJsonlFiles(r)));
17060
17047
  const files = fileLists.flat().sort().slice(0, numberOption(options.limit) || void 0);
17061
17048
  return Promise.all(files.map(async (filePath) => {
17062
- const info = await stat18(filePath);
17049
+ const info = await stat17(filePath);
17063
17050
  return { path: filePath, modifiedAt: info.mtime.toISOString() };
17064
17051
  }));
17065
17052
  }
@@ -17310,20 +17297,36 @@ async function sendSessionRollupBatch(rollups, options, ctx) {
17310
17297
  machine,
17311
17298
  allowHistoricalRewrite: options.force === true
17312
17299
  });
17313
- if (options.force && result.conflicts === 0) {
17314
- for (const rollup of rollups) {
17315
- for (const sessionId of rollup.supersededSessionIds ?? []) {
17316
- try {
17317
- await deleteRollupsBySource(remote, rollup.source, machine, {
17318
- rollupKey: createImportKey(["rollup", rollup.source, sessionId]),
17319
- allowHistoricalRewrite: options.force === true
17320
- });
17321
- } catch (error) {
17322
- debug(ctx, `Failed to delete superseded ${rollup.source} session ${sessionId}: ${error.message}
17300
+ if (result.conflicts === 0) {
17301
+ if (options.force) {
17302
+ for (const rollup of rollups) {
17303
+ for (const sessionId of rollup.supersededSessionIds ?? []) {
17304
+ try {
17305
+ await deleteRollupsBySource(remote, rollup.source, machine, {
17306
+ rollupKey: rollupKeyForSourceSession(rollup.source, sessionId),
17307
+ allowHistoricalRewrite: true
17308
+ });
17309
+ } catch (error) {
17310
+ debug(ctx, `Failed to delete superseded ${rollup.source} session ${sessionId}: ${error.message}
17323
17311
  `);
17312
+ }
17324
17313
  }
17325
17314
  }
17326
17315
  }
17316
+ for (const rollup of rollups) {
17317
+ if (rollup.source !== "cursor") {
17318
+ continue;
17319
+ }
17320
+ try {
17321
+ await deleteRollupsBySource(remote, "grok-bot", machine, {
17322
+ rollupKey: rollupKeyForSourceSession("grok-bot", rollup.sessionId),
17323
+ allowHistoricalRewrite: true
17324
+ });
17325
+ } catch (error) {
17326
+ debug(ctx, `Failed to delete grok-bot twin of cursor session ${rollup.sessionId}: ${error.message}
17327
+ `);
17328
+ }
17329
+ }
17327
17330
  }
17328
17331
  return result;
17329
17332
  }
@@ -17611,7 +17614,7 @@ function syncLocalRunnerEntryArgs(cliPath) {
17611
17614
  return [path27.resolve(path27.dirname(cliPath), "../bin/vibetime.mjs")];
17612
17615
  }
17613
17616
  function resolveHome3(options, ctx) {
17614
- return path27.resolve(stringOption(options.home) || ctx.env.HOME || os14.homedir());
17617
+ return path27.resolve(stringOption(options.home) || ctx.env.HOME || os13.homedir());
17615
17618
  }
17616
17619
  function requestedTargets(options) {
17617
17620
  const value = options.target || options.targets;
@@ -17621,7 +17624,13 @@ function requestedTargets(options) {
17621
17624
  return valuesOption(value).flatMap((item) => item.split(",")).map((t) => t.trim()).filter(Boolean);
17622
17625
  }
17623
17626
  function normalizeBackfillSource(source) {
17624
- return source === "claude" ? "claude-code" : source;
17627
+ if (source === "claude") {
17628
+ return "claude-code";
17629
+ }
17630
+ if (source === "grok-bot") {
17631
+ return "cursor";
17632
+ }
17633
+ return source;
17625
17634
  }
17626
17635
  function requiredOption(options, name) {
17627
17636
  const value = stringOption(options[name]);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.74",
4
+ "version": "0.1.75",
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": {