@yhong91/vibetime 0.1.57 → 0.1.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vibetime.mjs +201 -61
- 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
|
|
887
|
+
import { mkdir as mkdir5, open, rm, stat as stat16, writeFile as writeFile4 } 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.
|
|
2050
|
+
var PACKAGE_VERSION = true ? "0.1.58" : "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
|
|
5015
|
-
files.push({ path: filePath, modifiedAt:
|
|
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
|
-
|
|
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 =
|
|
5139
|
-
const
|
|
5140
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6295
|
+
import { copyFile, readdir as readdir6, readFile as readFile8, stat as stat8 } from "node:fs/promises";
|
|
6242
6296
|
import os6 from "node:os";
|
|
6243
6297
|
import path13 from "node:path";
|
|
6244
6298
|
|
|
@@ -6362,6 +6416,7 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
6362
6416
|
"subagentStart",
|
|
6363
6417
|
"subagentStop",
|
|
6364
6418
|
"stop",
|
|
6419
|
+
"afterAgentResponse",
|
|
6365
6420
|
"afterFileEdit",
|
|
6366
6421
|
"afterShellExecution",
|
|
6367
6422
|
"preCompact"
|
|
@@ -6778,7 +6833,7 @@ async function buildCursorChatMetaIndex(cursorHome2) {
|
|
|
6778
6833
|
return index;
|
|
6779
6834
|
}
|
|
6780
6835
|
async function readCursorStoreMeta(storePath) {
|
|
6781
|
-
const info = await
|
|
6836
|
+
const info = await stat8(storePath).catch(() => null);
|
|
6782
6837
|
if (!info) {
|
|
6783
6838
|
return {};
|
|
6784
6839
|
}
|
|
@@ -6851,7 +6906,7 @@ async function parseCursorTranscriptFile(filePath, options) {
|
|
|
6851
6906
|
}
|
|
6852
6907
|
const sessionId = path13.basename(filePath, ".jsonl");
|
|
6853
6908
|
const context = await resolveTranscriptContext(filePath, sessionId);
|
|
6854
|
-
const fileInfo = await
|
|
6909
|
+
const fileInfo = await stat8(filePath).catch(() => null);
|
|
6855
6910
|
let cwd = context.cwd;
|
|
6856
6911
|
let project = context.project;
|
|
6857
6912
|
let model = context.model;
|
|
@@ -7062,6 +7117,7 @@ async function parseCursorTranscriptFile(filePath, options) {
|
|
|
7062
7117
|
}
|
|
7063
7118
|
}
|
|
7064
7119
|
const endedAt = latestTimestamp(lastTs, context.endedAt, fileMtime);
|
|
7120
|
+
const lastTurnId = currentTurnId;
|
|
7065
7121
|
if (endedAt) {
|
|
7066
7122
|
closeTurn(endedAt);
|
|
7067
7123
|
if (sessionStarted) {
|
|
@@ -7073,11 +7129,59 @@ async function parseCursorTranscriptFile(filePath, options) {
|
|
|
7073
7129
|
}, "transcript");
|
|
7074
7130
|
}
|
|
7075
7131
|
}
|
|
7132
|
+
await appendPersistedCursorUsage({
|
|
7133
|
+
push,
|
|
7134
|
+
sessionId,
|
|
7135
|
+
options,
|
|
7136
|
+
fallbackTs: endedAt || lastTs,
|
|
7137
|
+
lastTurnId,
|
|
7138
|
+
model,
|
|
7139
|
+
skip: events.some((event) => event.type === "model.usage")
|
|
7140
|
+
});
|
|
7076
7141
|
return events;
|
|
7077
7142
|
}
|
|
7143
|
+
async function appendPersistedCursorUsage(args) {
|
|
7144
|
+
if (args.skip) {
|
|
7145
|
+
return;
|
|
7146
|
+
}
|
|
7147
|
+
const persisted = await readPersistedSessionContextFromOptions(args.options, args.sessionId);
|
|
7148
|
+
emitPersistedCursorUsage(
|
|
7149
|
+
args.push,
|
|
7150
|
+
persisted?.usage,
|
|
7151
|
+
args.fallbackTs,
|
|
7152
|
+
args.lastTurnId,
|
|
7153
|
+
args.model,
|
|
7154
|
+
args.sessionId
|
|
7155
|
+
);
|
|
7156
|
+
}
|
|
7157
|
+
function emitPersistedCursorUsage(push, usage, fallbackTs, lastTurnId, model, sessionId, skip = false) {
|
|
7158
|
+
if (skip || !usage?.length || !fallbackTs) {
|
|
7159
|
+
return;
|
|
7160
|
+
}
|
|
7161
|
+
for (const item of usage) {
|
|
7162
|
+
const metrics = usageMetrics({
|
|
7163
|
+
inputTokens: item.tokensInput,
|
|
7164
|
+
outputTokens: item.tokensOutput,
|
|
7165
|
+
cacheReadTokens: item.tokensCacheReadInput,
|
|
7166
|
+
cacheWriteTokens: item.tokensCacheCreationInput
|
|
7167
|
+
});
|
|
7168
|
+
if (!metrics) {
|
|
7169
|
+
continue;
|
|
7170
|
+
}
|
|
7171
|
+
push({
|
|
7172
|
+
ts: timestampFrom(item.ts) || fallbackTs,
|
|
7173
|
+
type: "model.usage",
|
|
7174
|
+
turnId: lastTurnId,
|
|
7175
|
+
model: item.model || model,
|
|
7176
|
+
confidence: "exact",
|
|
7177
|
+
metrics,
|
|
7178
|
+
refs: stringRefs({ sourceId: `${sessionId}:${item.generationId}:usage` })
|
|
7179
|
+
}, "hook-usage");
|
|
7180
|
+
}
|
|
7181
|
+
}
|
|
7078
7182
|
async function listCursorComposerIds(dbPath) {
|
|
7079
7183
|
const ids = /* @__PURE__ */ new Set();
|
|
7080
|
-
const info = await
|
|
7184
|
+
const info = await stat8(dbPath).catch(() => null);
|
|
7081
7185
|
if (!info) {
|
|
7082
7186
|
return ids;
|
|
7083
7187
|
}
|
|
@@ -7096,7 +7200,17 @@ async function listCursorComposerIds(dbPath) {
|
|
|
7096
7200
|
}
|
|
7097
7201
|
return ids;
|
|
7098
7202
|
}
|
|
7099
|
-
async function
|
|
7203
|
+
async function modifiedAtWithHookUsage(fileMtime, home, sessionIds) {
|
|
7204
|
+
let latest = fileMtime.getTime();
|
|
7205
|
+
for (const sessionId of sessionIds) {
|
|
7206
|
+
const extra = await persistedSessionContextModifiedAt(home, sessionId);
|
|
7207
|
+
if (extra && extra.getTime() > latest) {
|
|
7208
|
+
latest = extra.getTime();
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
7211
|
+
return new Date(latest).toISOString();
|
|
7212
|
+
}
|
|
7213
|
+
async function collectCursorTranscriptFiles(root, home, skipSessionIds) {
|
|
7100
7214
|
const files = [];
|
|
7101
7215
|
const seen = /* @__PURE__ */ new Set();
|
|
7102
7216
|
for (const filePath of await listJsonlFiles(root)) {
|
|
@@ -7108,11 +7222,14 @@ async function collectCursorTranscriptFiles(root, skipSessionIds) {
|
|
|
7108
7222
|
continue;
|
|
7109
7223
|
}
|
|
7110
7224
|
seen.add(sessionId);
|
|
7111
|
-
const info = await
|
|
7225
|
+
const info = await stat8(filePath).catch(() => null);
|
|
7112
7226
|
if (!info) {
|
|
7113
7227
|
continue;
|
|
7114
7228
|
}
|
|
7115
|
-
files.push({
|
|
7229
|
+
files.push({
|
|
7230
|
+
path: filePath,
|
|
7231
|
+
modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, [sessionId])
|
|
7232
|
+
});
|
|
7116
7233
|
}
|
|
7117
7234
|
return files;
|
|
7118
7235
|
}
|
|
@@ -7133,7 +7250,8 @@ async function parseCursorSessionFile(filePath, options) {
|
|
|
7133
7250
|
try {
|
|
7134
7251
|
const composers = listComposers(opened.db);
|
|
7135
7252
|
for (const composer of composers) {
|
|
7136
|
-
|
|
7253
|
+
const persisted = await readPersistedSessionContextFromOptions(options, composer.composerId);
|
|
7254
|
+
events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options, persisted?.usage));
|
|
7137
7255
|
}
|
|
7138
7256
|
} finally {
|
|
7139
7257
|
opened.db.close();
|
|
@@ -7141,7 +7259,7 @@ async function parseCursorSessionFile(filePath, options) {
|
|
|
7141
7259
|
}
|
|
7142
7260
|
return events;
|
|
7143
7261
|
}
|
|
7144
|
-
function parseComposer(db, composer, filePath, sourcePathHash, options) {
|
|
7262
|
+
function parseComposer(db, composer, filePath, sourcePathHash, options, persistedUsage) {
|
|
7145
7263
|
const sessionId = composer.composerId;
|
|
7146
7264
|
const data = composer.data;
|
|
7147
7265
|
const { cwd, project } = workspaceFromComposer(data);
|
|
@@ -7351,54 +7469,76 @@ function parseComposer(db, composer, filePath, sourcePathHash, options) {
|
|
|
7351
7469
|
refs: stringRefs({ sourceId: `${sessionId}:ended` })
|
|
7352
7470
|
}, "composer");
|
|
7353
7471
|
}
|
|
7472
|
+
emitPersistedCursorUsage(push, persistedUsage, endedAt || startedAt, currentTurnId, model, sessionId, events.some((event) => event.type === "model.usage"));
|
|
7354
7473
|
return events;
|
|
7355
7474
|
}
|
|
7356
7475
|
async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
|
|
7357
7476
|
if (sourceRoot) {
|
|
7358
|
-
const info = await
|
|
7477
|
+
const info = await stat8(sourceRoot).catch(() => null);
|
|
7359
7478
|
if (!info) {
|
|
7360
7479
|
return [];
|
|
7361
7480
|
}
|
|
7362
7481
|
if (info.isDirectory()) {
|
|
7363
7482
|
const files2 = [];
|
|
7483
|
+
const skipIds2 = /* @__PURE__ */ new Set();
|
|
7364
7484
|
for (const dbPath of await listFilesByExtensions(sourceRoot, [".vscdb", ".db"])) {
|
|
7365
7485
|
const base = path13.basename(dbPath);
|
|
7366
7486
|
if (base === "store.db" || base !== "state.vscdb" && !base.endsWith(".vscdb")) {
|
|
7367
7487
|
continue;
|
|
7368
7488
|
}
|
|
7369
|
-
const dbInfo = await
|
|
7370
|
-
if (dbInfo) {
|
|
7371
|
-
|
|
7489
|
+
const dbInfo = await stat8(dbPath).catch(() => null);
|
|
7490
|
+
if (!dbInfo) {
|
|
7491
|
+
continue;
|
|
7372
7492
|
}
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
for (const file of files2) {
|
|
7376
|
-
for (const id of await listCursorComposerIds(file.path)) {
|
|
7493
|
+
const composerIds2 = await listCursorComposerIds(dbPath);
|
|
7494
|
+
for (const id of composerIds2) {
|
|
7377
7495
|
skipIds2.add(id);
|
|
7378
7496
|
}
|
|
7497
|
+
files2.push({
|
|
7498
|
+
path: dbPath,
|
|
7499
|
+
modifiedAt: await modifiedAtWithHookUsage(dbInfo.mtime, home, composerIds2)
|
|
7500
|
+
});
|
|
7379
7501
|
}
|
|
7380
|
-
files2.push(...await collectCursorTranscriptFiles(sourceRoot, skipIds2));
|
|
7502
|
+
files2.push(...await collectCursorTranscriptFiles(sourceRoot, home, skipIds2));
|
|
7381
7503
|
return files2;
|
|
7382
7504
|
}
|
|
7383
7505
|
if (path13.basename(sourceRoot) === "store.db") {
|
|
7384
7506
|
return [];
|
|
7385
7507
|
}
|
|
7386
|
-
|
|
7508
|
+
if (sourceRoot.endsWith(".jsonl")) {
|
|
7509
|
+
return [{
|
|
7510
|
+
path: sourceRoot,
|
|
7511
|
+
modifiedAt: await modifiedAtWithHookUsage(
|
|
7512
|
+
info.mtime,
|
|
7513
|
+
home,
|
|
7514
|
+
[path13.basename(sourceRoot, ".jsonl")]
|
|
7515
|
+
)
|
|
7516
|
+
}];
|
|
7517
|
+
}
|
|
7518
|
+
const composerIds = await listCursorComposerIds(sourceRoot);
|
|
7519
|
+
return [{
|
|
7520
|
+
path: sourceRoot,
|
|
7521
|
+
modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, composerIds)
|
|
7522
|
+
}];
|
|
7387
7523
|
}
|
|
7388
7524
|
const files = [];
|
|
7389
7525
|
const skipIds = /* @__PURE__ */ new Set();
|
|
7390
7526
|
for (const candidatePath of cursorStateDbCandidates(home, env)) {
|
|
7391
|
-
const info = await
|
|
7527
|
+
const info = await stat8(candidatePath).catch(() => null);
|
|
7392
7528
|
if (!info) {
|
|
7393
7529
|
continue;
|
|
7394
7530
|
}
|
|
7395
|
-
|
|
7396
|
-
for (const id of
|
|
7531
|
+
const composerIds = await listCursorComposerIds(candidatePath);
|
|
7532
|
+
for (const id of composerIds) {
|
|
7397
7533
|
skipIds.add(id);
|
|
7398
7534
|
}
|
|
7535
|
+
files.push({
|
|
7536
|
+
path: candidatePath,
|
|
7537
|
+
modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, composerIds)
|
|
7538
|
+
});
|
|
7399
7539
|
break;
|
|
7400
7540
|
}
|
|
7401
|
-
files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), skipIds));
|
|
7541
|
+
files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), home, skipIds));
|
|
7402
7542
|
return files;
|
|
7403
7543
|
}
|
|
7404
7544
|
function cursorHookConfig() {
|
|
@@ -7444,7 +7584,7 @@ function createCursorAdapter() {
|
|
|
7444
7584
|
}
|
|
7445
7585
|
|
|
7446
7586
|
// src/adapters/grok-build.ts
|
|
7447
|
-
import { readdir as readdir7, readFile as readFile9, stat as
|
|
7587
|
+
import { readdir as readdir7, readFile as readFile9, stat as stat9 } from "node:fs/promises";
|
|
7448
7588
|
import path14 from "node:path";
|
|
7449
7589
|
init_fs();
|
|
7450
7590
|
var GROK_COST_TICKS_PER_USD = 1e9;
|
|
@@ -7484,7 +7624,7 @@ async function grokBackfillFiles(sourceRoot, home, env) {
|
|
|
7484
7624
|
continue;
|
|
7485
7625
|
}
|
|
7486
7626
|
try {
|
|
7487
|
-
const info = await
|
|
7627
|
+
const info = await stat9(entryPath);
|
|
7488
7628
|
files.push({ path: entryPath, modifiedAt: info.mtime.toISOString() });
|
|
7489
7629
|
} catch {
|
|
7490
7630
|
}
|
|
@@ -9244,19 +9384,19 @@ function opencodeDataCandidates(home, env) {
|
|
|
9244
9384
|
return [primary, path16.join(home, ".opencode", "opencode.db")];
|
|
9245
9385
|
}
|
|
9246
9386
|
async function opencodeBackfillFiles(sourceRoot, home = os7.homedir(), env) {
|
|
9247
|
-
const { stat:
|
|
9387
|
+
const { stat: stat17 } = await import("node:fs/promises");
|
|
9248
9388
|
if (sourceRoot) {
|
|
9249
9389
|
if (!sourceRoot.endsWith(".db")) {
|
|
9250
9390
|
return [];
|
|
9251
9391
|
}
|
|
9252
|
-
const info = await
|
|
9392
|
+
const info = await stat17(sourceRoot).catch(() => null);
|
|
9253
9393
|
if (!info) {
|
|
9254
9394
|
return [];
|
|
9255
9395
|
}
|
|
9256
9396
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
9257
9397
|
}
|
|
9258
9398
|
for (const candidatePath of opencodeDataCandidates(home, env)) {
|
|
9259
|
-
const info = await
|
|
9399
|
+
const info = await stat17(candidatePath).catch(() => null);
|
|
9260
9400
|
if (info) {
|
|
9261
9401
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
9262
9402
|
}
|
|
@@ -9356,7 +9496,7 @@ function createOpenCodeAdapter() {
|
|
|
9356
9496
|
}
|
|
9357
9497
|
|
|
9358
9498
|
// src/adapters/pi.ts
|
|
9359
|
-
import { readdir as readdir8, readFile as readFile11, stat as
|
|
9499
|
+
import { readdir as readdir8, readFile as readFile11, stat as stat10 } from "node:fs/promises";
|
|
9360
9500
|
import os8 from "node:os";
|
|
9361
9501
|
import path17 from "node:path";
|
|
9362
9502
|
init_fs();
|
|
@@ -10052,7 +10192,7 @@ async function piBackfillFiles(sourceRoot, home = os8.homedir(), env) {
|
|
|
10052
10192
|
]);
|
|
10053
10193
|
const files = lists.flat().sort();
|
|
10054
10194
|
return Promise.all(files.map(async (filePath) => {
|
|
10055
|
-
const info = await
|
|
10195
|
+
const info = await stat10(filePath);
|
|
10056
10196
|
let groupId = resolvePiBackfillGroupId(filePath);
|
|
10057
10197
|
if (!groupId && filePath.endsWith(".json")) {
|
|
10058
10198
|
groupId = await workflowRunGroupId(filePath);
|
|
@@ -10099,7 +10239,7 @@ function createPiAdapter() {
|
|
|
10099
10239
|
}
|
|
10100
10240
|
|
|
10101
10241
|
// src/adapters/qoder-cn.ts
|
|
10102
|
-
import { readdir as readdir9, readFile as readFile12, stat as
|
|
10242
|
+
import { readdir as readdir9, readFile as readFile12, stat as stat11 } from "node:fs/promises";
|
|
10103
10243
|
import os10 from "node:os";
|
|
10104
10244
|
import path19 from "node:path";
|
|
10105
10245
|
|
|
@@ -10996,7 +11136,7 @@ async function gitRootFromCwds2(cwds) {
|
|
|
10996
11136
|
while (!seen.has(current)) {
|
|
10997
11137
|
seen.add(current);
|
|
10998
11138
|
try {
|
|
10999
|
-
await
|
|
11139
|
+
await stat11(path19.join(current, ".git"));
|
|
11000
11140
|
return current;
|
|
11001
11141
|
} catch {
|
|
11002
11142
|
}
|
|
@@ -11146,7 +11286,7 @@ function createQoderCnAdapter() {
|
|
|
11146
11286
|
|
|
11147
11287
|
// src/adapters/qoder.ts
|
|
11148
11288
|
import { existsSync } from "node:fs";
|
|
11149
|
-
import { readdir as readdir10, readFile as readFile13, stat as
|
|
11289
|
+
import { readdir as readdir10, readFile as readFile13, stat as stat12 } from "node:fs/promises";
|
|
11150
11290
|
import os11 from "node:os";
|
|
11151
11291
|
import path20 from "node:path";
|
|
11152
11292
|
function parseQoderPaths(filePath) {
|
|
@@ -11842,7 +11982,7 @@ async function gitRootFromCwds3(cwds) {
|
|
|
11842
11982
|
while (!seen.has(current)) {
|
|
11843
11983
|
seen.add(current);
|
|
11844
11984
|
try {
|
|
11845
|
-
await
|
|
11985
|
+
await stat12(path20.join(current, ".git"));
|
|
11846
11986
|
return current;
|
|
11847
11987
|
} catch {
|
|
11848
11988
|
}
|
|
@@ -12050,7 +12190,7 @@ function normalizeId(id) {
|
|
|
12050
12190
|
}
|
|
12051
12191
|
|
|
12052
12192
|
// src/adapters/workbuddy.ts
|
|
12053
|
-
import { readdir as readdir11, readFile as readFile14, stat as
|
|
12193
|
+
import { readdir as readdir11, readFile as readFile14, stat as stat13 } from "node:fs/promises";
|
|
12054
12194
|
import path21 from "node:path";
|
|
12055
12195
|
function workbuddyProjectsDir(home, env) {
|
|
12056
12196
|
const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
|
|
@@ -12503,7 +12643,7 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
|
|
|
12503
12643
|
for (const entry of entries) {
|
|
12504
12644
|
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
12505
12645
|
const filePath = path21.join(projectDir, entry.name);
|
|
12506
|
-
const info = await
|
|
12646
|
+
const info = await stat13(filePath);
|
|
12507
12647
|
files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
|
|
12508
12648
|
}
|
|
12509
12649
|
}
|
|
@@ -12564,7 +12704,7 @@ function createWorkbuddyAdapter() {
|
|
|
12564
12704
|
|
|
12565
12705
|
// src/adapters/zcode.ts
|
|
12566
12706
|
import { execFile } from "node:child_process";
|
|
12567
|
-
import { readFile as readFile15, stat as
|
|
12707
|
+
import { readFile as readFile15, stat as stat14 } from "node:fs/promises";
|
|
12568
12708
|
import path22 from "node:path";
|
|
12569
12709
|
import { promisify as promisify2 } from "node:util";
|
|
12570
12710
|
init_fs();
|
|
@@ -12583,7 +12723,7 @@ var providerNameCache = null;
|
|
|
12583
12723
|
async function loadProviderNames(configPath2) {
|
|
12584
12724
|
let fileMtime = 0;
|
|
12585
12725
|
try {
|
|
12586
|
-
const info = await
|
|
12726
|
+
const info = await stat14(configPath2);
|
|
12587
12727
|
fileMtime = info.mtimeMs;
|
|
12588
12728
|
} catch {
|
|
12589
12729
|
return /* @__PURE__ */ new Map();
|
|
@@ -12800,7 +12940,7 @@ async function parseZCodeDb(filePath, options) {
|
|
|
12800
12940
|
for (let i = 0; i < 12; i++) {
|
|
12801
12941
|
const probe = path22.join(candidate, ".zcode", "v2", "config.json");
|
|
12802
12942
|
try {
|
|
12803
|
-
await
|
|
12943
|
+
await stat14(probe);
|
|
12804
12944
|
configPath2 = probe;
|
|
12805
12945
|
break;
|
|
12806
12946
|
} catch {
|
|
@@ -13026,7 +13166,7 @@ async function zcodeBackfillFiles(sourceRoot, home, env) {
|
|
|
13026
13166
|
const candidate = sourceRoot || zcodeDbPath(home, env);
|
|
13027
13167
|
const filePath = candidate.endsWith(".sqlite") ? candidate : path22.join(candidate, "db", "db.sqlite");
|
|
13028
13168
|
try {
|
|
13029
|
-
const info = await
|
|
13169
|
+
const info = await stat14(filePath);
|
|
13030
13170
|
return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
|
|
13031
13171
|
} catch {
|
|
13032
13172
|
return [];
|
|
@@ -13411,19 +13551,19 @@ async function parseZedSessionFile(dbPath, options) {
|
|
|
13411
13551
|
return events.filter((event) => matchesBackfillFilters(event, options));
|
|
13412
13552
|
}
|
|
13413
13553
|
async function zedBackfillFiles(sourceRoot, home = os12.homedir(), env) {
|
|
13414
|
-
const { stat:
|
|
13554
|
+
const { stat: stat17 } = await import("node:fs/promises");
|
|
13415
13555
|
if (sourceRoot) {
|
|
13416
13556
|
if (!sourceRoot.endsWith(".db")) {
|
|
13417
13557
|
return [];
|
|
13418
13558
|
}
|
|
13419
|
-
const info = await
|
|
13559
|
+
const info = await stat17(sourceRoot).catch(() => null);
|
|
13420
13560
|
if (!info) {
|
|
13421
13561
|
return [];
|
|
13422
13562
|
}
|
|
13423
13563
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
13424
13564
|
}
|
|
13425
13565
|
for (const candidatePath of zedThreadsCandidates(home, env)) {
|
|
13426
|
-
const info = await
|
|
13566
|
+
const info = await stat17(candidatePath).catch(() => null);
|
|
13427
13567
|
if (info) {
|
|
13428
13568
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
13429
13569
|
}
|
|
@@ -14272,7 +14412,7 @@ function defaultMachineName() {
|
|
|
14272
14412
|
init_fs();
|
|
14273
14413
|
|
|
14274
14414
|
// src/lib/logger.ts
|
|
14275
|
-
import { appendFile, mkdir as mkdir4, rename, stat as
|
|
14415
|
+
import { appendFile, mkdir as mkdir4, rename, stat as stat15 } from "node:fs/promises";
|
|
14276
14416
|
import { homedir as homedir2 } from "node:os";
|
|
14277
14417
|
import path25 from "node:path";
|
|
14278
14418
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
@@ -14290,7 +14430,7 @@ function serializeError(error) {
|
|
|
14290
14430
|
}
|
|
14291
14431
|
async function rotateIfNeeded(file) {
|
|
14292
14432
|
try {
|
|
14293
|
-
const info = await
|
|
14433
|
+
const info = await stat15(file);
|
|
14294
14434
|
if (info.size > MAX_BYTES) {
|
|
14295
14435
|
await rename(file, `${file}.1`).catch(() => {
|
|
14296
14436
|
});
|
|
@@ -15114,7 +15254,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
15114
15254
|
const fileLists = await Promise.all(roots.map((r) => listJsonlFiles(r)));
|
|
15115
15255
|
const files = fileLists.flat().sort().slice(0, numberOption(options.limit) || void 0);
|
|
15116
15256
|
return Promise.all(files.map(async (filePath) => {
|
|
15117
|
-
const info = await
|
|
15257
|
+
const info = await stat16(filePath);
|
|
15118
15258
|
return { path: filePath, modifiedAt: info.mtime.toISOString() };
|
|
15119
15259
|
}));
|
|
15120
15260
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yhong91/vibetime",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.58",
|
|
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": {
|