@yhong91/vibetime 0.1.56 → 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 +1764 -345
- package/package.json +2 -2
package/bin/vibetime.mjs
CHANGED
|
@@ -884,9 +884,9 @@ 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
|
|
888
|
-
import
|
|
889
|
-
import
|
|
887
|
+
import { mkdir as mkdir5, open, rm, stat as stat16, writeFile as writeFile4 } from "node:fs/promises";
|
|
888
|
+
import os13 from "node:os";
|
|
889
|
+
import path26 from "node:path";
|
|
890
890
|
import { fileURLToPath } from "node:url";
|
|
891
891
|
|
|
892
892
|
// ../shared/src/index.ts
|
|
@@ -924,7 +924,7 @@ var TELEMETRY_EVENT_TYPES = [
|
|
|
924
924
|
"agent.operation"
|
|
925
925
|
];
|
|
926
926
|
var FILE_ACTIVITY_OPERATIONS = ["read", "search", "create", "write", "edit", "delete"];
|
|
927
|
-
var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "copilot", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode", "grok-build", "zed", "kimi-code"];
|
|
927
|
+
var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "copilot", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode", "grok-build", "zed", "kimi-code", "cursor"];
|
|
928
928
|
function createWorkspaceId(input) {
|
|
929
929
|
const basis = input.repoUrl || input.repoRoot || input.projectName || "unknown";
|
|
930
930
|
return `workspace_${fnv1a(basis)}`;
|
|
@@ -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) {
|
|
@@ -6168,95 +6222,1387 @@ function baseCopilotEvent(event) {
|
|
|
6168
6222
|
...event
|
|
6169
6223
|
};
|
|
6170
6224
|
}
|
|
6171
|
-
async function copilotBackfillFiles(sourceRoot, home = os5.homedir(), _env) {
|
|
6172
|
-
const sessionDir = sourceRoot || path12.join(home, ".copilot", "session-state");
|
|
6173
|
-
const results = [];
|
|
6174
|
-
let entries;
|
|
6175
|
-
try {
|
|
6176
|
-
entries = await readdir5(sessionDir);
|
|
6177
|
-
} catch {
|
|
6178
|
-
return [];
|
|
6225
|
+
async function copilotBackfillFiles(sourceRoot, home = os5.homedir(), _env) {
|
|
6226
|
+
const sessionDir = sourceRoot || path12.join(home, ".copilot", "session-state");
|
|
6227
|
+
const results = [];
|
|
6228
|
+
let entries;
|
|
6229
|
+
try {
|
|
6230
|
+
entries = await readdir5(sessionDir);
|
|
6231
|
+
} catch {
|
|
6232
|
+
return [];
|
|
6233
|
+
}
|
|
6234
|
+
for (const entry of entries) {
|
|
6235
|
+
if (entry.startsWith("pending-session")) {
|
|
6236
|
+
continue;
|
|
6237
|
+
}
|
|
6238
|
+
const eventsPath = path12.join(sessionDir, entry, "events.jsonl");
|
|
6239
|
+
const info = await stat7(eventsPath).catch(() => null);
|
|
6240
|
+
if (info) {
|
|
6241
|
+
results.push({ path: eventsPath, modifiedAt: info.mtime.toISOString() });
|
|
6242
|
+
}
|
|
6243
|
+
}
|
|
6244
|
+
return results;
|
|
6245
|
+
}
|
|
6246
|
+
function copilotPluginContent() {
|
|
6247
|
+
return `// Agent Time plugin for GitHub Copilot CLI
|
|
6248
|
+
// Generated by vibetime.
|
|
6249
|
+
// Copilot does not support hooks \u2014 this file is a placeholder for detection.
|
|
6250
|
+
// Backfill reads session data from ~/.copilot/session-state/*/events.jsonl.
|
|
6251
|
+
`;
|
|
6252
|
+
}
|
|
6253
|
+
function copilotHome(home, env) {
|
|
6254
|
+
const override = env?.COPILOT_HOME;
|
|
6255
|
+
if (override && override.trim()) {
|
|
6256
|
+
return path12.resolve(override);
|
|
6257
|
+
}
|
|
6258
|
+
return path12.join(home, ".copilot");
|
|
6259
|
+
}
|
|
6260
|
+
function createCopilotAdapter() {
|
|
6261
|
+
return {
|
|
6262
|
+
id: "copilot",
|
|
6263
|
+
label: "GitHub Copilot",
|
|
6264
|
+
agentName: "copilot",
|
|
6265
|
+
kind: "agent",
|
|
6266
|
+
detectPath(home, env) {
|
|
6267
|
+
return copilotHome(home, env);
|
|
6268
|
+
},
|
|
6269
|
+
installedPath(home, env) {
|
|
6270
|
+
return path12.join(copilotHome(home, env), ".vibetime");
|
|
6271
|
+
},
|
|
6272
|
+
async isInstalled(home, env) {
|
|
6273
|
+
try {
|
|
6274
|
+
const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
6275
|
+
return await pathExists2(path12.join(copilotHome(home, env), ".vibetime"));
|
|
6276
|
+
} catch {
|
|
6277
|
+
return false;
|
|
6278
|
+
}
|
|
6279
|
+
},
|
|
6280
|
+
installEntries(home, env) {
|
|
6281
|
+
return [{
|
|
6282
|
+
kind: "file",
|
|
6283
|
+
path: path12.join(copilotHome(home, env), ".vibetime"),
|
|
6284
|
+
content: copilotPluginContent()
|
|
6285
|
+
}];
|
|
6286
|
+
},
|
|
6287
|
+
sourcePaths(home, env) {
|
|
6288
|
+
return [path12.join(copilotHome(home, env), "session-state")];
|
|
6289
|
+
},
|
|
6290
|
+
parseSessionFile: parseCopilotSessionFile
|
|
6291
|
+
};
|
|
6292
|
+
}
|
|
6293
|
+
|
|
6294
|
+
// src/adapters/cursor.ts
|
|
6295
|
+
import { copyFile, readdir as readdir6, readFile as readFile8, stat as stat8 } from "node:fs/promises";
|
|
6296
|
+
import os6 from "node:os";
|
|
6297
|
+
import path13 from "node:path";
|
|
6298
|
+
|
|
6299
|
+
// src/lib/cursor-hooks.ts
|
|
6300
|
+
function isCursorHooksFile(value) {
|
|
6301
|
+
if (!isPlainObject(value)) {
|
|
6302
|
+
return false;
|
|
6303
|
+
}
|
|
6304
|
+
if (value.version !== 1 && value.version !== void 0) {
|
|
6305
|
+
return false;
|
|
6306
|
+
}
|
|
6307
|
+
return isPlainObject(value.hooks);
|
|
6308
|
+
}
|
|
6309
|
+
function hookCommandFromCursorEntry(entry) {
|
|
6310
|
+
if (!isPlainObject(entry) || typeof entry.command !== "string" || !entry.command) {
|
|
6311
|
+
return void 0;
|
|
6312
|
+
}
|
|
6313
|
+
return entry.command;
|
|
6314
|
+
}
|
|
6315
|
+
function hasCursorHookCommand(config, command) {
|
|
6316
|
+
if (!isCursorHooksFile(config)) {
|
|
6317
|
+
return false;
|
|
6318
|
+
}
|
|
6319
|
+
return Object.values(config.hooks).some(
|
|
6320
|
+
(entries) => Array.isArray(entries) && entries.some((entry) => hookCommandFromCursorEntry(entry) === command)
|
|
6321
|
+
);
|
|
6322
|
+
}
|
|
6323
|
+
function mergeCursorHooksJson(existing, addition) {
|
|
6324
|
+
const mergedHooks = {};
|
|
6325
|
+
const existingHooks = isCursorHooksFile(existing) ? existing.hooks : {};
|
|
6326
|
+
for (const [event, entries] of Object.entries(existingHooks)) {
|
|
6327
|
+
if (Array.isArray(entries)) {
|
|
6328
|
+
mergedHooks[event] = [...entries];
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
for (const [event, entries] of Object.entries(addition.hooks)) {
|
|
6332
|
+
const current = mergedHooks[event] ? [...mergedHooks[event]] : [];
|
|
6333
|
+
for (const entry of entries) {
|
|
6334
|
+
const command = hookCommandFromCursorEntry(entry);
|
|
6335
|
+
if (!command) {
|
|
6336
|
+
continue;
|
|
6337
|
+
}
|
|
6338
|
+
const alreadyPresent = current.some(
|
|
6339
|
+
(existingEntry) => hookCommandFromCursorEntry(existingEntry) === command
|
|
6340
|
+
);
|
|
6341
|
+
if (!alreadyPresent) {
|
|
6342
|
+
current.push(entry);
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
mergedHooks[event] = current;
|
|
6346
|
+
}
|
|
6347
|
+
const extra = isPlainObject(existing) ? Object.fromEntries(
|
|
6348
|
+
Object.entries(existing).filter(([key]) => key !== "version" && key !== "hooks")
|
|
6349
|
+
) : {};
|
|
6350
|
+
return {
|
|
6351
|
+
...extra,
|
|
6352
|
+
version: 1,
|
|
6353
|
+
hooks: mergedHooks
|
|
6354
|
+
};
|
|
6355
|
+
}
|
|
6356
|
+
function stripCursorHookCommands(existing, commands) {
|
|
6357
|
+
const next = structuredClone(isPlainObject(existing) ? existing : {});
|
|
6358
|
+
if (!isPlainObject(next.hooks)) {
|
|
6359
|
+
return { next, changed: false };
|
|
6360
|
+
}
|
|
6361
|
+
const hooks = { ...next.hooks };
|
|
6362
|
+
let changed = false;
|
|
6363
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
6364
|
+
if (!Array.isArray(entries)) {
|
|
6365
|
+
continue;
|
|
6366
|
+
}
|
|
6367
|
+
const stripped = entries.filter((entry) => {
|
|
6368
|
+
const command = hookCommandFromCursorEntry(entry);
|
|
6369
|
+
return !command || !commands.has(command);
|
|
6370
|
+
});
|
|
6371
|
+
if (stripped.length !== entries.length) {
|
|
6372
|
+
changed = true;
|
|
6373
|
+
}
|
|
6374
|
+
if (stripped.length === 0) {
|
|
6375
|
+
delete hooks[event];
|
|
6376
|
+
} else {
|
|
6377
|
+
hooks[event] = stripped;
|
|
6378
|
+
}
|
|
6379
|
+
}
|
|
6380
|
+
if (Object.keys(hooks).length === 0) {
|
|
6381
|
+
delete next.hooks;
|
|
6382
|
+
changed = true;
|
|
6383
|
+
} else {
|
|
6384
|
+
next.hooks = hooks;
|
|
6385
|
+
}
|
|
6386
|
+
return { next, changed };
|
|
6387
|
+
}
|
|
6388
|
+
function collectCursorHookCommands(content) {
|
|
6389
|
+
const commands = /* @__PURE__ */ new Set();
|
|
6390
|
+
for (const entries of Object.values(content.hooks)) {
|
|
6391
|
+
for (const entry of entries) {
|
|
6392
|
+
const command = hookCommandFromCursorEntry(entry);
|
|
6393
|
+
if (command) {
|
|
6394
|
+
commands.add(command);
|
|
6395
|
+
}
|
|
6396
|
+
}
|
|
6397
|
+
}
|
|
6398
|
+
return [...commands];
|
|
6399
|
+
}
|
|
6400
|
+
|
|
6401
|
+
// src/adapters/cursor.ts
|
|
6402
|
+
init_fs();
|
|
6403
|
+
var SOURCE_ID = "cursor";
|
|
6404
|
+
var AGENT_NAME = "cursor";
|
|
6405
|
+
var HOOK_COMMAND = `vibetime hook --agent ${SOURCE_ID}`;
|
|
6406
|
+
var HOOK_TIMEOUT_SECONDS = 10;
|
|
6407
|
+
var BUBBLE_USER = 1;
|
|
6408
|
+
var CAPABILITY_TOOL = 15;
|
|
6409
|
+
var CURSOR_HOOK_EVENTS = [
|
|
6410
|
+
"sessionStart",
|
|
6411
|
+
"sessionEnd",
|
|
6412
|
+
"beforeSubmitPrompt",
|
|
6413
|
+
"preToolUse",
|
|
6414
|
+
"postToolUse",
|
|
6415
|
+
"postToolUseFailure",
|
|
6416
|
+
"subagentStart",
|
|
6417
|
+
"subagentStop",
|
|
6418
|
+
"stop",
|
|
6419
|
+
"afterAgentResponse",
|
|
6420
|
+
"afterFileEdit",
|
|
6421
|
+
"afterShellExecution",
|
|
6422
|
+
"preCompact"
|
|
6423
|
+
];
|
|
6424
|
+
function cursorHome(home, env) {
|
|
6425
|
+
const override = env?.CURSOR_HOME;
|
|
6426
|
+
if (override && override.trim()) {
|
|
6427
|
+
return path13.resolve(override);
|
|
6428
|
+
}
|
|
6429
|
+
return path13.join(home, ".cursor");
|
|
6430
|
+
}
|
|
6431
|
+
function cursorHooksPath(home, env) {
|
|
6432
|
+
return path13.join(cursorHome(home, env), "hooks.json");
|
|
6433
|
+
}
|
|
6434
|
+
function cursorProjectsDir(home, env) {
|
|
6435
|
+
return path13.join(cursorHome(home, env), "projects");
|
|
6436
|
+
}
|
|
6437
|
+
function cursorChatsDir(home, env) {
|
|
6438
|
+
return path13.join(cursorHome(home, env), "chats");
|
|
6439
|
+
}
|
|
6440
|
+
function cursorUserDir(home, env) {
|
|
6441
|
+
const override = env?.CURSOR_USER_DIR;
|
|
6442
|
+
if (override && override.trim()) {
|
|
6443
|
+
return path13.resolve(override);
|
|
6444
|
+
}
|
|
6445
|
+
return void 0;
|
|
6446
|
+
}
|
|
6447
|
+
function cursorStateDbCandidates(home, env) {
|
|
6448
|
+
const candidates = [];
|
|
6449
|
+
const userDir = cursorUserDir(home, env);
|
|
6450
|
+
if (userDir) {
|
|
6451
|
+
candidates.push(path13.join(userDir, "globalStorage", "state.vscdb"));
|
|
6452
|
+
}
|
|
6453
|
+
const platform2 = process.platform;
|
|
6454
|
+
if (platform2 === "darwin") {
|
|
6455
|
+
candidates.push(path13.join(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb"));
|
|
6456
|
+
} else if (platform2 === "win32") {
|
|
6457
|
+
const appdata = env?.APPDATA;
|
|
6458
|
+
if (appdata && appdata.trim()) {
|
|
6459
|
+
candidates.push(path13.join(path13.resolve(appdata), "Cursor", "User", "globalStorage", "state.vscdb"));
|
|
6460
|
+
}
|
|
6461
|
+
candidates.push(path13.join(home, "AppData", "Roaming", "Cursor", "User", "globalStorage", "state.vscdb"));
|
|
6462
|
+
} else {
|
|
6463
|
+
const xdgConfig = env?.XDG_CONFIG_HOME;
|
|
6464
|
+
if (xdgConfig && xdgConfig.trim()) {
|
|
6465
|
+
candidates.push(path13.join(path13.resolve(xdgConfig), "Cursor", "User", "globalStorage", "state.vscdb"));
|
|
6466
|
+
}
|
|
6467
|
+
candidates.push(path13.join(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb"));
|
|
6468
|
+
}
|
|
6469
|
+
return candidates;
|
|
6470
|
+
}
|
|
6471
|
+
function decodeKv(value) {
|
|
6472
|
+
if (typeof value === "string") {
|
|
6473
|
+
return value;
|
|
6474
|
+
}
|
|
6475
|
+
if (value instanceof Uint8Array) {
|
|
6476
|
+
return new TextDecoder().decode(value);
|
|
6477
|
+
}
|
|
6478
|
+
return void 0;
|
|
6479
|
+
}
|
|
6480
|
+
function parseJsonObject(text) {
|
|
6481
|
+
if (!text) {
|
|
6482
|
+
return void 0;
|
|
6483
|
+
}
|
|
6484
|
+
try {
|
|
6485
|
+
const parsed = JSON.parse(text);
|
|
6486
|
+
return isPlainObject(parsed) ? parsed : void 0;
|
|
6487
|
+
} catch {
|
|
6488
|
+
return void 0;
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6491
|
+
function parseMaybeJsonObject(value) {
|
|
6492
|
+
if (isPlainObject(value)) {
|
|
6493
|
+
return value;
|
|
6494
|
+
}
|
|
6495
|
+
if (typeof value === "string") {
|
|
6496
|
+
return parseJsonObject(value) || {};
|
|
6497
|
+
}
|
|
6498
|
+
return {};
|
|
6499
|
+
}
|
|
6500
|
+
function cleanId(value) {
|
|
6501
|
+
return value.replace(/\s+/g, "_").slice(0, 200);
|
|
6502
|
+
}
|
|
6503
|
+
async function openCursorDb(dbPath) {
|
|
6504
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
6505
|
+
try {
|
|
6506
|
+
return {
|
|
6507
|
+
db: new DatabaseSync(dbPath, { readOnly: true }),
|
|
6508
|
+
cleanup: async () => {
|
|
6509
|
+
}
|
|
6510
|
+
};
|
|
6511
|
+
} catch {
|
|
6512
|
+
const tmp = path13.join(os6.tmpdir(), `vibetime-cursor-${createStableHash(dbPath).slice(0, 12)}.db`);
|
|
6513
|
+
await copyFile(dbPath, tmp);
|
|
6514
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
6515
|
+
await copyFile(`${dbPath}${suffix}`, `${tmp}${suffix}`).catch(() => void 0);
|
|
6516
|
+
}
|
|
6517
|
+
return {
|
|
6518
|
+
db: new DatabaseSync(tmp, { readOnly: true }),
|
|
6519
|
+
cleanup: async () => {
|
|
6520
|
+
const { unlink } = await import("node:fs/promises");
|
|
6521
|
+
await unlink(tmp).catch(() => void 0);
|
|
6522
|
+
await unlink(`${tmp}-wal`).catch(() => void 0);
|
|
6523
|
+
await unlink(`${tmp}-shm`).catch(() => void 0);
|
|
6524
|
+
}
|
|
6525
|
+
};
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6528
|
+
function listComposers(db) {
|
|
6529
|
+
const dataById = /* @__PURE__ */ new Map();
|
|
6530
|
+
const dataRows = db.prepare(
|
|
6531
|
+
"SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'"
|
|
6532
|
+
).all();
|
|
6533
|
+
for (const row of dataRows) {
|
|
6534
|
+
const id = row.key.slice("composerData:".length);
|
|
6535
|
+
const data = parseJsonObject(decodeKv(row.value));
|
|
6536
|
+
if (id && data) {
|
|
6537
|
+
dataById.set(id, data);
|
|
6538
|
+
}
|
|
6539
|
+
}
|
|
6540
|
+
let headers = [];
|
|
6541
|
+
try {
|
|
6542
|
+
headers = db.prepare(
|
|
6543
|
+
"SELECT composerId, createdAt, lastUpdatedAt, isSubagent, value FROM composerHeaders"
|
|
6544
|
+
).all();
|
|
6545
|
+
} catch {
|
|
6546
|
+
headers = [...dataById.entries()].map(([composerId, data]) => ({
|
|
6547
|
+
composerId,
|
|
6548
|
+
createdAt: numberField(data, "createdAt"),
|
|
6549
|
+
lastUpdatedAt: numberField(data, "lastUpdatedAt"),
|
|
6550
|
+
isSubagent: data.isBestOfNSubcomposer || data.isSubagent ? 1 : 0,
|
|
6551
|
+
value: JSON.stringify(data)
|
|
6552
|
+
}));
|
|
6553
|
+
}
|
|
6554
|
+
const composers = [];
|
|
6555
|
+
for (const headerRow of headers) {
|
|
6556
|
+
const composerId = headerRow.composerId;
|
|
6557
|
+
if (!composerId || composerId === "empty-state-draft") {
|
|
6558
|
+
continue;
|
|
6559
|
+
}
|
|
6560
|
+
const header = parseJsonObject(decodeKv(headerRow.value)) || parseMaybeJsonObject(headerRow.value);
|
|
6561
|
+
const data = dataById.get(composerId) || header;
|
|
6562
|
+
if (data.isDraft === true || data.isEphemeral === true || header.isDraft === true || header.isEphemeral === true) {
|
|
6563
|
+
continue;
|
|
6564
|
+
}
|
|
6565
|
+
composers.push({
|
|
6566
|
+
composerId,
|
|
6567
|
+
createdAt: headerRow.createdAt ?? numberField(data, "createdAt") ?? numberField(header, "createdAt"),
|
|
6568
|
+
lastUpdatedAt: headerRow.lastUpdatedAt ?? numberField(data, "lastUpdatedAt") ?? numberField(header, "lastUpdatedAt"),
|
|
6569
|
+
isSubagent: Boolean(headerRow.isSubagent) || data.isBestOfNSubcomposer === true,
|
|
6570
|
+
header,
|
|
6571
|
+
data
|
|
6572
|
+
});
|
|
6573
|
+
}
|
|
6574
|
+
return composers;
|
|
6575
|
+
}
|
|
6576
|
+
function loadBubbles(db, composerId) {
|
|
6577
|
+
const rows = db.prepare(
|
|
6578
|
+
"SELECT key, value FROM cursorDiskKV WHERE key LIKE ?"
|
|
6579
|
+
).all(`bubbleId:${composerId}:%`);
|
|
6580
|
+
const bubbles = [];
|
|
6581
|
+
for (const row of rows) {
|
|
6582
|
+
const bubble = parseJsonObject(decodeKv(row.value));
|
|
6583
|
+
if (bubble) {
|
|
6584
|
+
bubbles.push(bubble);
|
|
6585
|
+
}
|
|
6586
|
+
}
|
|
6587
|
+
bubbles.sort((a, b) => {
|
|
6588
|
+
const aTs = stringField(a, "createdAt") || "";
|
|
6589
|
+
const bTs = stringField(b, "createdAt") || "";
|
|
6590
|
+
return aTs.localeCompare(bTs);
|
|
6591
|
+
});
|
|
6592
|
+
return bubbles;
|
|
6593
|
+
}
|
|
6594
|
+
function workspaceFromComposer(data) {
|
|
6595
|
+
const ident = objectField(data, "workspaceIdentifier");
|
|
6596
|
+
const uri = objectField(ident, "uri");
|
|
6597
|
+
const cwd = stringField(uri, "fsPath") || stringField(uri, "path");
|
|
6598
|
+
return {
|
|
6599
|
+
cwd,
|
|
6600
|
+
project: cwd ? path13.basename(cwd) : void 0
|
|
6601
|
+
};
|
|
6602
|
+
}
|
|
6603
|
+
function modelFromComposer(data) {
|
|
6604
|
+
const config = objectField(data, "modelConfig");
|
|
6605
|
+
return stringField(config, "modelName") || stringField(config, "modelId");
|
|
6606
|
+
}
|
|
6607
|
+
function bubbleTs(bubble, fallback) {
|
|
6608
|
+
return timestampFrom(bubble.createdAt) || stringField(bubble, "createdAt") || fallback;
|
|
6609
|
+
}
|
|
6610
|
+
function toolNameOf(raw) {
|
|
6611
|
+
if (!raw) {
|
|
6612
|
+
return "unknown";
|
|
6613
|
+
}
|
|
6614
|
+
return raw.replace(/_v\d+$/i, "");
|
|
6615
|
+
}
|
|
6616
|
+
function isShellTool(name) {
|
|
6617
|
+
const lower = name.toLowerCase();
|
|
6618
|
+
return lower === "shell" || lower === "bash" || lower === "run_terminal_cmd" || lower === "run_terminal_command";
|
|
6619
|
+
}
|
|
6620
|
+
function fileActivitiesFromCursorTool(toolName, input, ts, cwd) {
|
|
6621
|
+
const lower = toolName.toLowerCase();
|
|
6622
|
+
if (isShellTool(toolName)) {
|
|
6623
|
+
const command = stringField(input, "command") || stringField(input, "cmd");
|
|
6624
|
+
if (command) {
|
|
6625
|
+
return fileActivitiesFromShellCommand(command, ts, cwd, cwd);
|
|
6626
|
+
}
|
|
6627
|
+
return [];
|
|
6628
|
+
}
|
|
6629
|
+
const target = stringField(input, "path") || stringField(input, "targetFile") || stringField(input, "target_file") || stringField(input, "file_path") || stringField(input, "filePath");
|
|
6630
|
+
const display = target ? displayFilePath(target, cwd) : void 0;
|
|
6631
|
+
if (lower === "read" || lower === "read_file" || lower === "readfile") {
|
|
6632
|
+
if (!display) {
|
|
6633
|
+
return [];
|
|
6634
|
+
}
|
|
6635
|
+
return [{ ts, path: display, operation: "read", confidence: "exact" }];
|
|
6636
|
+
}
|
|
6637
|
+
if (lower === "grep" || lower === "grep_search" || lower === "ripgrep" || lower === "glob" || lower === "glob_file_search" || lower === "globfilesearch") {
|
|
6638
|
+
if (!display) {
|
|
6639
|
+
return [];
|
|
6640
|
+
}
|
|
6641
|
+
return [{ ts, path: display, operation: "search", confidence: "exact" }];
|
|
6642
|
+
}
|
|
6643
|
+
if (lower === "delete" || lower === "delete_file") {
|
|
6644
|
+
if (!display) {
|
|
6645
|
+
return [];
|
|
6646
|
+
}
|
|
6647
|
+
return [{ ts, path: display, operation: "delete", confidence: "exact" }];
|
|
6648
|
+
}
|
|
6649
|
+
if (lower === "write" || lower === "write_file") {
|
|
6650
|
+
if (!display) {
|
|
6651
|
+
return [];
|
|
6652
|
+
}
|
|
6653
|
+
const content = stringField(input, "contents") || stringField(input, "content");
|
|
6654
|
+
return [{
|
|
6655
|
+
ts,
|
|
6656
|
+
path: display,
|
|
6657
|
+
operation: "write",
|
|
6658
|
+
confidence: "exact",
|
|
6659
|
+
linesAdded: countTextLines(content),
|
|
6660
|
+
charsWritten: content?.length
|
|
6661
|
+
}];
|
|
6662
|
+
}
|
|
6663
|
+
if (lower === "search_replace" || lower === "strreplace" || lower === "edit" || lower === "edit_file" || lower === "apply_patch" || lower === "applypatch") {
|
|
6664
|
+
if (!display) {
|
|
6665
|
+
return [];
|
|
6666
|
+
}
|
|
6667
|
+
const oldString = stringField(input, "old_string") || stringField(input, "oldString") || stringField(input, "old_str");
|
|
6668
|
+
const newString = stringField(input, "new_string") || stringField(input, "newString") || stringField(input, "new_str") || stringField(input, "contents") || stringField(input, "content");
|
|
6669
|
+
return [{
|
|
6670
|
+
ts,
|
|
6671
|
+
path: display,
|
|
6672
|
+
operation: "edit",
|
|
6673
|
+
confidence: "exact",
|
|
6674
|
+
linesAdded: countTextLines(newString),
|
|
6675
|
+
linesRemoved: countTextLines(oldString),
|
|
6676
|
+
charsWritten: newString?.length
|
|
6677
|
+
}];
|
|
6678
|
+
}
|
|
6679
|
+
return [];
|
|
6680
|
+
}
|
|
6681
|
+
function usageMetrics(tokenCount) {
|
|
6682
|
+
const inputTokens = numberField(tokenCount, "inputTokens") || 0;
|
|
6683
|
+
const outputTokens = numberField(tokenCount, "outputTokens") || 0;
|
|
6684
|
+
const cached = numberField(tokenCount, "cacheReadTokens") || numberField(tokenCount, "cachedInputTokens") || 0;
|
|
6685
|
+
const cacheCreation = numberField(tokenCount, "cacheWriteTokens") || numberField(tokenCount, "cacheCreationTokens") || 0;
|
|
6686
|
+
if (inputTokens <= 0 && outputTokens <= 0 && cached <= 0 && cacheCreation <= 0) {
|
|
6687
|
+
return void 0;
|
|
6688
|
+
}
|
|
6689
|
+
return {
|
|
6690
|
+
tokensInput: inputTokens || void 0,
|
|
6691
|
+
tokensOutput: outputTokens || void 0,
|
|
6692
|
+
tokensCachedInput: cached + cacheCreation || void 0,
|
|
6693
|
+
tokensCacheReadInput: cached || void 0,
|
|
6694
|
+
tokensCacheCreationInput: cacheCreation || void 0,
|
|
6695
|
+
tokensTotal: inputTokens + outputTokens || void 0,
|
|
6696
|
+
modelCalls: 1
|
|
6697
|
+
};
|
|
6698
|
+
}
|
|
6699
|
+
var CURSOR_TIMESTAMP_RE = /<timestamp>\s*([^<]+?)\s*<\/timestamp>/i;
|
|
6700
|
+
var CURSOR_USER_QUERY_RE = /<user_query>\s*([\s\S]*?)\s*<\/user_query>/i;
|
|
6701
|
+
var chatMetaIndexCache = /* @__PURE__ */ new Map();
|
|
6702
|
+
function isCursorTranscriptPath(filePath) {
|
|
6703
|
+
return filePath.split(path13.sep).includes("agent-transcripts") && filePath.endsWith(".jsonl");
|
|
6704
|
+
}
|
|
6705
|
+
function cursorHomeFromTranscript(filePath) {
|
|
6706
|
+
const parts = filePath.split(path13.sep);
|
|
6707
|
+
const at = parts.lastIndexOf("agent-transcripts");
|
|
6708
|
+
if (at >= 2 && parts[at - 2] === "projects") {
|
|
6709
|
+
return parts.slice(0, at - 2).join(path13.sep);
|
|
6710
|
+
}
|
|
6711
|
+
return void 0;
|
|
6712
|
+
}
|
|
6713
|
+
function parseCursorClock(raw) {
|
|
6714
|
+
const trimmed = raw.trim();
|
|
6715
|
+
const direct = timestampFrom(trimmed);
|
|
6716
|
+
if (direct) {
|
|
6717
|
+
return direct;
|
|
6718
|
+
}
|
|
6719
|
+
const withoutWeekday = trimmed.replace(/^[A-Za-z]{3,9},\s*/, "");
|
|
6720
|
+
const normalized = withoutWeekday.replace(/\((UTC[+-]\d{1,2}(?::\d{2})?)\)/i, "$1");
|
|
6721
|
+
for (const candidate of [normalized, withoutWeekday, trimmed]) {
|
|
6722
|
+
const parsed = Date.parse(candidate);
|
|
6723
|
+
if (!Number.isNaN(parsed)) {
|
|
6724
|
+
return new Date(parsed).toISOString();
|
|
6725
|
+
}
|
|
6726
|
+
}
|
|
6727
|
+
return void 0;
|
|
6728
|
+
}
|
|
6729
|
+
function latestTimestamp(...values) {
|
|
6730
|
+
let best;
|
|
6731
|
+
for (const value of values) {
|
|
6732
|
+
if (!value || Number.isNaN(Date.parse(value))) {
|
|
6733
|
+
continue;
|
|
6734
|
+
}
|
|
6735
|
+
if (!best || value > best) {
|
|
6736
|
+
best = value;
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
return best;
|
|
6740
|
+
}
|
|
6741
|
+
function timestampBetween(start, end, index, count) {
|
|
6742
|
+
if (count <= 0 || index < 0) {
|
|
6743
|
+
return start;
|
|
6744
|
+
}
|
|
6745
|
+
const startMs = Date.parse(start);
|
|
6746
|
+
const endMs = Date.parse(end);
|
|
6747
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs <= startMs) {
|
|
6748
|
+
return start;
|
|
6749
|
+
}
|
|
6750
|
+
return new Date(startMs + Math.round((endMs - startMs) * (index + 1) / (count + 1))).toISOString();
|
|
6751
|
+
}
|
|
6752
|
+
function parseCursorUserText(text) {
|
|
6753
|
+
const tsMatch = text.match(CURSOR_TIMESTAMP_RE);
|
|
6754
|
+
const queryMatch = text.match(CURSOR_USER_QUERY_RE);
|
|
6755
|
+
return {
|
|
6756
|
+
ts: tsMatch?.[1] ? parseCursorClock(tsMatch[1]) : void 0,
|
|
6757
|
+
prompt: (queryMatch?.[1] || text.replace(CURSOR_TIMESTAMP_RE, "")).trim()
|
|
6758
|
+
};
|
|
6759
|
+
}
|
|
6760
|
+
function messageText(message) {
|
|
6761
|
+
const content = message.content;
|
|
6762
|
+
if (typeof content === "string") {
|
|
6763
|
+
return content;
|
|
6764
|
+
}
|
|
6765
|
+
if (!Array.isArray(content)) {
|
|
6766
|
+
return stringField(message, "text") || "";
|
|
6767
|
+
}
|
|
6768
|
+
const parts = [];
|
|
6769
|
+
for (const item of content) {
|
|
6770
|
+
if (isPlainObject(item) && stringField(item, "type") === "text") {
|
|
6771
|
+
const text = stringField(item, "text");
|
|
6772
|
+
if (text) {
|
|
6773
|
+
parts.push(text);
|
|
6774
|
+
}
|
|
6775
|
+
}
|
|
6776
|
+
}
|
|
6777
|
+
return parts.join("\n");
|
|
6778
|
+
}
|
|
6779
|
+
function messageToolUses(message) {
|
|
6780
|
+
const content = message.content;
|
|
6781
|
+
if (!Array.isArray(content)) {
|
|
6782
|
+
return [];
|
|
6783
|
+
}
|
|
6784
|
+
const tools = [];
|
|
6785
|
+
for (const item of content) {
|
|
6786
|
+
if (!isPlainObject(item) || stringField(item, "type") !== "tool_use") {
|
|
6787
|
+
continue;
|
|
6788
|
+
}
|
|
6789
|
+
tools.push({
|
|
6790
|
+
name: stringField(item, "name") || "unknown",
|
|
6791
|
+
id: stringField(item, "id"),
|
|
6792
|
+
input: isPlainObject(item.input) ? item.input : parseMaybeJsonObject(item.input)
|
|
6793
|
+
});
|
|
6794
|
+
}
|
|
6795
|
+
return tools;
|
|
6796
|
+
}
|
|
6797
|
+
async function indexCursorChatMeta(cursorHome2) {
|
|
6798
|
+
const cached = chatMetaIndexCache.get(cursorHome2);
|
|
6799
|
+
if (cached) {
|
|
6800
|
+
return cached;
|
|
6801
|
+
}
|
|
6802
|
+
const pending = buildCursorChatMetaIndex(cursorHome2);
|
|
6803
|
+
chatMetaIndexCache.set(cursorHome2, pending);
|
|
6804
|
+
return pending;
|
|
6805
|
+
}
|
|
6806
|
+
async function buildCursorChatMetaIndex(cursorHome2) {
|
|
6807
|
+
const index = /* @__PURE__ */ new Map();
|
|
6808
|
+
const chatsDir = path13.join(cursorHome2, "chats");
|
|
6809
|
+
const projectDirs = await readdir6(chatsDir, { withFileTypes: true }).catch(() => []);
|
|
6810
|
+
for (const project of projectDirs) {
|
|
6811
|
+
if (!project.isDirectory()) {
|
|
6812
|
+
continue;
|
|
6813
|
+
}
|
|
6814
|
+
const sessions = await readdir6(path13.join(chatsDir, project.name), { withFileTypes: true }).catch(() => []);
|
|
6815
|
+
for (const session of sessions) {
|
|
6816
|
+
if (!session.isDirectory()) {
|
|
6817
|
+
continue;
|
|
6818
|
+
}
|
|
6819
|
+
const sessionDir = path13.join(chatsDir, project.name, session.name);
|
|
6820
|
+
const meta = await readJsonIfExists(path13.join(sessionDir, "meta.json"));
|
|
6821
|
+
const fromMeta = isPlainObject(meta) ? meta : {};
|
|
6822
|
+
const fromStore = await readCursorStoreMeta(path13.join(sessionDir, "store.db"));
|
|
6823
|
+
const cwd = stringField(fromMeta, "cwd") || fromStore.cwd;
|
|
6824
|
+
index.set(session.name, {
|
|
6825
|
+
cwd,
|
|
6826
|
+
project: cwd ? path13.basename(cwd) : void 0,
|
|
6827
|
+
model: fromStore.model,
|
|
6828
|
+
startedAt: timestampFrom(fromMeta.createdAtMs) || fromStore.startedAt,
|
|
6829
|
+
endedAt: timestampFrom(fromMeta.updatedAtMs) || fromStore.endedAt
|
|
6830
|
+
});
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
return index;
|
|
6834
|
+
}
|
|
6835
|
+
async function readCursorStoreMeta(storePath) {
|
|
6836
|
+
const info = await stat8(storePath).catch(() => null);
|
|
6837
|
+
if (!info) {
|
|
6838
|
+
return {};
|
|
6839
|
+
}
|
|
6840
|
+
try {
|
|
6841
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
6842
|
+
const db = new DatabaseSync(storePath, { readOnly: true });
|
|
6843
|
+
try {
|
|
6844
|
+
const row = db.prepare("SELECT value FROM meta LIMIT 1").get();
|
|
6845
|
+
const decoded = decodeStoreMetaValue(row?.value);
|
|
6846
|
+
if (!decoded) {
|
|
6847
|
+
return {};
|
|
6848
|
+
}
|
|
6849
|
+
const cwd = stringField(decoded, "cwd") || stringField(objectField(decoded, "workspace"), "path");
|
|
6850
|
+
return {
|
|
6851
|
+
cwd,
|
|
6852
|
+
project: cwd ? path13.basename(cwd) : void 0,
|
|
6853
|
+
model: stringField(decoded, "lastUsedModel") || stringField(decoded, "model"),
|
|
6854
|
+
startedAt: timestampFrom(decoded.createdAt),
|
|
6855
|
+
endedAt: timestampFrom(decoded.lastUpdatedAt) || timestampFrom(decoded.updatedAt)
|
|
6856
|
+
};
|
|
6857
|
+
} finally {
|
|
6858
|
+
db.close();
|
|
6859
|
+
}
|
|
6860
|
+
} catch {
|
|
6861
|
+
return {};
|
|
6862
|
+
}
|
|
6863
|
+
}
|
|
6864
|
+
function decodeStoreMetaValue(value) {
|
|
6865
|
+
if (typeof value !== "string" || !value) {
|
|
6866
|
+
return void 0;
|
|
6867
|
+
}
|
|
6868
|
+
try {
|
|
6869
|
+
const asJson = parseJsonObject(value);
|
|
6870
|
+
if (asJson) {
|
|
6871
|
+
return asJson;
|
|
6872
|
+
}
|
|
6873
|
+
return parseJsonObject(Buffer.from(value, "hex").toString("utf8"));
|
|
6874
|
+
} catch {
|
|
6875
|
+
return void 0;
|
|
6876
|
+
}
|
|
6877
|
+
}
|
|
6878
|
+
async function resolveTranscriptContext(filePath, sessionId) {
|
|
6879
|
+
const sibling = await readJsonIfExists(path13.join(path13.dirname(filePath), "meta.json"));
|
|
6880
|
+
if (isPlainObject(sibling)) {
|
|
6881
|
+
const cwd = stringField(sibling, "cwd");
|
|
6882
|
+
return {
|
|
6883
|
+
cwd,
|
|
6884
|
+
project: cwd ? path13.basename(cwd) : void 0,
|
|
6885
|
+
startedAt: timestampFrom(sibling.createdAtMs),
|
|
6886
|
+
endedAt: timestampFrom(sibling.updatedAtMs)
|
|
6887
|
+
};
|
|
6888
|
+
}
|
|
6889
|
+
const cursorHomePath = cursorHomeFromTranscript(filePath);
|
|
6890
|
+
if (!cursorHomePath) {
|
|
6891
|
+
return {};
|
|
6892
|
+
}
|
|
6893
|
+
const index = await indexCursorChatMeta(cursorHomePath);
|
|
6894
|
+
return index.get(sessionId) || {};
|
|
6895
|
+
}
|
|
6896
|
+
async function parseCursorTranscriptFile(filePath, options) {
|
|
6897
|
+
const text = await readFile8(filePath, "utf8").catch(() => "");
|
|
6898
|
+
if (!text) {
|
|
6899
|
+
return [];
|
|
6900
|
+
}
|
|
6901
|
+
const lines = text.split("\n").filter(Boolean);
|
|
6902
|
+
const parsed = lines.map(parseJsonLine);
|
|
6903
|
+
const hasConversation = parsed.some((raw) => raw && (raw.role === "user" || raw.role === "assistant"));
|
|
6904
|
+
if (!hasConversation) {
|
|
6905
|
+
return [];
|
|
6906
|
+
}
|
|
6907
|
+
const sessionId = path13.basename(filePath, ".jsonl");
|
|
6908
|
+
const context = await resolveTranscriptContext(filePath, sessionId);
|
|
6909
|
+
const fileInfo = await stat8(filePath).catch(() => null);
|
|
6910
|
+
let cwd = context.cwd;
|
|
6911
|
+
let project = context.project;
|
|
6912
|
+
let model = context.model;
|
|
6913
|
+
let lastTs = context.startedAt || (fileInfo ? fileInfo.mtime.toISOString() : void 0);
|
|
6914
|
+
const fileMtime = fileInfo ? fileInfo.mtime.toISOString() : void 0;
|
|
6915
|
+
const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
|
|
6916
|
+
const sourcePathHash = `sha256:${createStableHash(filePath)}`;
|
|
6917
|
+
const events = [];
|
|
6918
|
+
let lineNumber = 0;
|
|
6919
|
+
let currentTurnId;
|
|
6920
|
+
let currentTurnStartedAt;
|
|
6921
|
+
let turnIndex = 0;
|
|
6922
|
+
let sessionStarted = false;
|
|
6923
|
+
let pendingTurnSuccess = true;
|
|
6924
|
+
const pendingTools = [];
|
|
6925
|
+
const push = (partial, topType) => {
|
|
6926
|
+
lineNumber += 1;
|
|
6927
|
+
const event = {
|
|
6928
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6929
|
+
source: SOURCE_ID,
|
|
6930
|
+
agent: AGENT_NAME,
|
|
6931
|
+
workspaceId,
|
|
6932
|
+
project,
|
|
6933
|
+
cwd,
|
|
6934
|
+
model,
|
|
6935
|
+
sessionId,
|
|
6936
|
+
...partial
|
|
6937
|
+
};
|
|
6938
|
+
events.push(withBackfillRefs(event, {
|
|
6939
|
+
filePath,
|
|
6940
|
+
sourcePathHash,
|
|
6941
|
+
lineNumber,
|
|
6942
|
+
topType,
|
|
6943
|
+
payloadType: event.type,
|
|
6944
|
+
options
|
|
6945
|
+
}));
|
|
6946
|
+
};
|
|
6947
|
+
const ensureSessionStarted = (ts) => {
|
|
6948
|
+
if (sessionStarted) {
|
|
6949
|
+
return;
|
|
6950
|
+
}
|
|
6951
|
+
sessionStarted = true;
|
|
6952
|
+
push({
|
|
6953
|
+
ts,
|
|
6954
|
+
type: "session.started",
|
|
6955
|
+
confidence: "partial",
|
|
6956
|
+
refs: stringRefs({ sourceId: `${sessionId}:started` })
|
|
6957
|
+
}, "transcript");
|
|
6958
|
+
};
|
|
6959
|
+
const closeTurn = (endTs, success = pendingTurnSuccess) => {
|
|
6960
|
+
if (!currentTurnId || !currentTurnStartedAt) {
|
|
6961
|
+
pendingTools.length = 0;
|
|
6962
|
+
pendingTurnSuccess = true;
|
|
6963
|
+
return;
|
|
6964
|
+
}
|
|
6965
|
+
const turnId = currentTurnId;
|
|
6966
|
+
const startedAt = currentTurnStartedAt;
|
|
6967
|
+
const finishedAt = latestTimestamp(startedAt, endTs) || startedAt;
|
|
6968
|
+
const toolCount = pendingTools.length;
|
|
6969
|
+
for (const [index, item] of pendingTools.entries()) {
|
|
6970
|
+
const ts = timestampBetween(startedAt, finishedAt, index, toolCount);
|
|
6971
|
+
const fileActivities = fileActivitiesFromCursorTool(item.tool, item.input, ts, cwd);
|
|
6972
|
+
push({
|
|
6973
|
+
ts,
|
|
6974
|
+
type: "tool.started",
|
|
6975
|
+
turnId,
|
|
6976
|
+
tool: item.tool,
|
|
6977
|
+
operation: `${item.tool} started`,
|
|
6978
|
+
model,
|
|
6979
|
+
confidence: "exact",
|
|
6980
|
+
fileActivities: fileActivities.length > 0 ? fileActivities : void 0,
|
|
6981
|
+
refs: stringRefs({ sourceId: `${item.toolCallId}:started` })
|
|
6982
|
+
}, "tool");
|
|
6983
|
+
push({
|
|
6984
|
+
ts,
|
|
6985
|
+
type: "tool.completed",
|
|
6986
|
+
turnId,
|
|
6987
|
+
tool: item.tool,
|
|
6988
|
+
operation: `${item.tool} completed`,
|
|
6989
|
+
success,
|
|
6990
|
+
model,
|
|
6991
|
+
confidence: "exact",
|
|
6992
|
+
metrics: { toolCalls: 1 },
|
|
6993
|
+
refs: stringRefs({ sourceId: `${item.toolCallId}:completed` })
|
|
6994
|
+
}, "tool");
|
|
6995
|
+
if (item.command) {
|
|
6996
|
+
push({
|
|
6997
|
+
ts,
|
|
6998
|
+
type: "command.started",
|
|
6999
|
+
turnId,
|
|
7000
|
+
tool: item.tool,
|
|
7001
|
+
operation: "command started",
|
|
7002
|
+
confidence: "derived",
|
|
7003
|
+
refs: stringRefs({
|
|
7004
|
+
sourceId: `${item.toolCallId}:command:started`,
|
|
7005
|
+
commandHash: `sha256:${createStableHash(item.command)}`
|
|
7006
|
+
})
|
|
7007
|
+
}, "tool");
|
|
7008
|
+
push({
|
|
7009
|
+
ts,
|
|
7010
|
+
type: "command.completed",
|
|
7011
|
+
turnId,
|
|
7012
|
+
tool: item.tool,
|
|
7013
|
+
operation: "command completed",
|
|
7014
|
+
success,
|
|
7015
|
+
confidence: "derived",
|
|
7016
|
+
metrics: { commandCalls: 1 },
|
|
7017
|
+
refs: stringRefs({ sourceId: `${item.toolCallId}:command:completed` })
|
|
7018
|
+
}, "tool");
|
|
7019
|
+
}
|
|
7020
|
+
}
|
|
7021
|
+
pendingTools.length = 0;
|
|
7022
|
+
push({
|
|
7023
|
+
ts: finishedAt,
|
|
7024
|
+
type: "turn.completed",
|
|
7025
|
+
turnId,
|
|
7026
|
+
model,
|
|
7027
|
+
success,
|
|
7028
|
+
confidence: "derived",
|
|
7029
|
+
metrics: { durationMs: durationMsBetween(startedAt, finishedAt) },
|
|
7030
|
+
refs: stringRefs({ sourceId: `${sessionId}:${turnId}:completed` })
|
|
7031
|
+
}, "transcript");
|
|
7032
|
+
currentTurnId = void 0;
|
|
7033
|
+
currentTurnStartedAt = void 0;
|
|
7034
|
+
pendingTurnSuccess = true;
|
|
7035
|
+
};
|
|
7036
|
+
for (const raw of parsed) {
|
|
7037
|
+
if (!raw) {
|
|
7038
|
+
continue;
|
|
7039
|
+
}
|
|
7040
|
+
const role = stringField(raw, "role");
|
|
7041
|
+
const topType = stringField(raw, "type");
|
|
7042
|
+
const message = objectField(raw, "message");
|
|
7043
|
+
const body = Object.keys(message).length > 0 ? message : raw;
|
|
7044
|
+
const rawText = messageText(body);
|
|
7045
|
+
if (!cwd) {
|
|
7046
|
+
const workspaceMatch = rawText.match(/Workspace Path:\s*(.+)/);
|
|
7047
|
+
const found = workspaceMatch?.[1]?.trim();
|
|
7048
|
+
if (found) {
|
|
7049
|
+
cwd = found;
|
|
7050
|
+
project = path13.basename(found);
|
|
7051
|
+
}
|
|
7052
|
+
}
|
|
7053
|
+
if (role === "user") {
|
|
7054
|
+
const userBits = parseCursorUserText(rawText);
|
|
7055
|
+
const ts = timestampFrom(raw.timestamp) || userBits.ts;
|
|
7056
|
+
if (!ts) {
|
|
7057
|
+
continue;
|
|
7058
|
+
}
|
|
7059
|
+
lastTs = latestTimestamp(lastTs, ts) || ts;
|
|
7060
|
+
ensureSessionStarted(ts);
|
|
7061
|
+
closeTurn(ts);
|
|
7062
|
+
turnIndex += 1;
|
|
7063
|
+
currentTurnId = `turn_${turnIndex}`;
|
|
7064
|
+
currentTurnStartedAt = ts;
|
|
7065
|
+
pendingTurnSuccess = true;
|
|
7066
|
+
push({
|
|
7067
|
+
ts,
|
|
7068
|
+
type: "turn.started",
|
|
7069
|
+
turnId: currentTurnId,
|
|
7070
|
+
model,
|
|
7071
|
+
confidence: "partial",
|
|
7072
|
+
refs: stringRefs({ sourceId: `${sessionId}:${currentTurnId}:started` })
|
|
7073
|
+
}, "transcript");
|
|
7074
|
+
if (userBits.prompt) {
|
|
7075
|
+
push({
|
|
7076
|
+
ts,
|
|
7077
|
+
type: "prompt.submitted",
|
|
7078
|
+
turnId: currentTurnId,
|
|
7079
|
+
model,
|
|
7080
|
+
confidence: "partial",
|
|
7081
|
+
metrics: { prompts: 1, promptChars: userBits.prompt.length },
|
|
7082
|
+
refs: stringRefs({
|
|
7083
|
+
sourceId: `${sessionId}:${currentTurnId}:prompt`,
|
|
7084
|
+
promptHash: `sha256:${createStableHash(userBits.prompt)}`
|
|
7085
|
+
})
|
|
7086
|
+
}, "transcript");
|
|
7087
|
+
}
|
|
7088
|
+
continue;
|
|
7089
|
+
}
|
|
7090
|
+
if (role === "assistant") {
|
|
7091
|
+
if (!currentTurnId && lastTs) {
|
|
7092
|
+
ensureSessionStarted(lastTs);
|
|
7093
|
+
turnIndex += 1;
|
|
7094
|
+
currentTurnId = `turn_${turnIndex}`;
|
|
7095
|
+
currentTurnStartedAt = lastTs;
|
|
7096
|
+
}
|
|
7097
|
+
if (!currentTurnId) {
|
|
7098
|
+
continue;
|
|
7099
|
+
}
|
|
7100
|
+
for (const [index, toolUse] of messageToolUses(body).entries()) {
|
|
7101
|
+
const tool = toolNameOf(toolUse.name);
|
|
7102
|
+
const command = isShellTool(tool) ? stringField(toolUse.input, "command") || stringField(toolUse.input, "cmd") : void 0;
|
|
7103
|
+
pendingTools.push({
|
|
7104
|
+
tool,
|
|
7105
|
+
toolCallId: cleanId(
|
|
7106
|
+
toolUse.id || `${sessionId}:${currentTurnId}:${pendingTools.length}:${index}`
|
|
7107
|
+
),
|
|
7108
|
+
input: toolUse.input,
|
|
7109
|
+
command
|
|
7110
|
+
});
|
|
7111
|
+
}
|
|
7112
|
+
continue;
|
|
7113
|
+
}
|
|
7114
|
+
if (topType === "turn_ended") {
|
|
7115
|
+
const status = (stringField(raw, "status") || "success").toLowerCase();
|
|
7116
|
+
pendingTurnSuccess = status !== "error" && status !== "failed" && status !== "cancelled";
|
|
7117
|
+
}
|
|
7118
|
+
}
|
|
7119
|
+
const endedAt = latestTimestamp(lastTs, context.endedAt, fileMtime);
|
|
7120
|
+
const lastTurnId = currentTurnId;
|
|
7121
|
+
if (endedAt) {
|
|
7122
|
+
closeTurn(endedAt);
|
|
7123
|
+
if (sessionStarted) {
|
|
7124
|
+
push({
|
|
7125
|
+
ts: endedAt,
|
|
7126
|
+
type: "session.ended",
|
|
7127
|
+
confidence: "derived",
|
|
7128
|
+
refs: stringRefs({ sourceId: `${sessionId}:ended` })
|
|
7129
|
+
}, "transcript");
|
|
7130
|
+
}
|
|
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
|
+
});
|
|
7141
|
+
return events;
|
|
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
|
+
}
|
|
7182
|
+
async function listCursorComposerIds(dbPath) {
|
|
7183
|
+
const ids = /* @__PURE__ */ new Set();
|
|
7184
|
+
const info = await stat8(dbPath).catch(() => null);
|
|
7185
|
+
if (!info) {
|
|
7186
|
+
return ids;
|
|
7187
|
+
}
|
|
7188
|
+
try {
|
|
7189
|
+
const opened = await openCursorDb(dbPath);
|
|
7190
|
+
try {
|
|
7191
|
+
for (const composer of listComposers(opened.db)) {
|
|
7192
|
+
ids.add(composer.composerId);
|
|
7193
|
+
}
|
|
7194
|
+
} finally {
|
|
7195
|
+
opened.db.close();
|
|
7196
|
+
await opened.cleanup();
|
|
7197
|
+
}
|
|
7198
|
+
} catch {
|
|
7199
|
+
return ids;
|
|
7200
|
+
}
|
|
7201
|
+
return ids;
|
|
7202
|
+
}
|
|
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) {
|
|
7214
|
+
const files = [];
|
|
7215
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7216
|
+
for (const filePath of await listJsonlFiles(root)) {
|
|
7217
|
+
if (!isCursorTranscriptPath(filePath) && path13.basename(path13.dirname(filePath)) !== "agent-transcripts") {
|
|
7218
|
+
continue;
|
|
7219
|
+
}
|
|
7220
|
+
const sessionId = path13.basename(filePath, ".jsonl");
|
|
7221
|
+
if (!sessionId || seen.has(sessionId) || skipSessionIds?.has(sessionId)) {
|
|
7222
|
+
continue;
|
|
7223
|
+
}
|
|
7224
|
+
seen.add(sessionId);
|
|
7225
|
+
const info = await stat8(filePath).catch(() => null);
|
|
7226
|
+
if (!info) {
|
|
7227
|
+
continue;
|
|
7228
|
+
}
|
|
7229
|
+
files.push({
|
|
7230
|
+
path: filePath,
|
|
7231
|
+
modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, [sessionId])
|
|
7232
|
+
});
|
|
7233
|
+
}
|
|
7234
|
+
return files;
|
|
7235
|
+
}
|
|
7236
|
+
async function parseCursorSessionFile(filePath, options) {
|
|
7237
|
+
const base = path13.basename(filePath);
|
|
7238
|
+
if (base.endsWith(".jsonl")) {
|
|
7239
|
+
return parseCursorTranscriptFile(filePath, options);
|
|
7240
|
+
}
|
|
7241
|
+
if (base === "store.db") {
|
|
7242
|
+
return [];
|
|
7243
|
+
}
|
|
7244
|
+
if (base !== "state.vscdb" && !base.endsWith(".vscdb") && !base.endsWith(".db")) {
|
|
7245
|
+
return [];
|
|
7246
|
+
}
|
|
7247
|
+
const opened = await openCursorDb(filePath);
|
|
7248
|
+
const events = [];
|
|
7249
|
+
const sourcePathHash = `sha256:${createStableHash(filePath)}`;
|
|
7250
|
+
try {
|
|
7251
|
+
const composers = listComposers(opened.db);
|
|
7252
|
+
for (const composer of composers) {
|
|
7253
|
+
const persisted = await readPersistedSessionContextFromOptions(options, composer.composerId);
|
|
7254
|
+
events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options, persisted?.usage));
|
|
7255
|
+
}
|
|
7256
|
+
} finally {
|
|
7257
|
+
opened.db.close();
|
|
7258
|
+
await opened.cleanup();
|
|
7259
|
+
}
|
|
7260
|
+
return events;
|
|
7261
|
+
}
|
|
7262
|
+
function parseComposer(db, composer, filePath, sourcePathHash, options, persistedUsage) {
|
|
7263
|
+
const sessionId = composer.composerId;
|
|
7264
|
+
const data = composer.data;
|
|
7265
|
+
const { cwd, project } = workspaceFromComposer(data);
|
|
7266
|
+
const model = modelFromComposer(data);
|
|
7267
|
+
const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
|
|
7268
|
+
const startedAt = timestampFrom(composer.createdAt) || timestampFrom(data.createdAt);
|
|
7269
|
+
const endedAt = timestampFrom(composer.lastUpdatedAt) || timestampFrom(data.lastUpdatedAt) || startedAt;
|
|
7270
|
+
if (!startedAt) {
|
|
7271
|
+
return [];
|
|
7272
|
+
}
|
|
7273
|
+
const bubbles = loadBubbles(db, sessionId);
|
|
7274
|
+
const events = [];
|
|
7275
|
+
let lineNumber = 0;
|
|
7276
|
+
let currentTurnId;
|
|
7277
|
+
let currentTurnStartedAt;
|
|
7278
|
+
let turnIndex = 0;
|
|
7279
|
+
let hasFileLineData = false;
|
|
7280
|
+
const push = (partial, topType) => {
|
|
7281
|
+
lineNumber += 1;
|
|
7282
|
+
const event = {
|
|
7283
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7284
|
+
source: SOURCE_ID,
|
|
7285
|
+
agent: AGENT_NAME,
|
|
7286
|
+
workspaceId,
|
|
7287
|
+
project,
|
|
7288
|
+
cwd,
|
|
7289
|
+
model,
|
|
7290
|
+
sessionId,
|
|
7291
|
+
...partial
|
|
7292
|
+
};
|
|
7293
|
+
events.push(withBackfillRefs(event, {
|
|
7294
|
+
filePath,
|
|
7295
|
+
sourcePathHash,
|
|
7296
|
+
lineNumber,
|
|
7297
|
+
topType,
|
|
7298
|
+
payloadType: event.type,
|
|
7299
|
+
options
|
|
7300
|
+
}));
|
|
7301
|
+
};
|
|
7302
|
+
push({
|
|
7303
|
+
ts: startedAt,
|
|
7304
|
+
type: "session.started",
|
|
7305
|
+
confidence: "partial",
|
|
7306
|
+
refs: stringRefs({ sourceId: `${sessionId}:started` })
|
|
7307
|
+
}, "composer");
|
|
7308
|
+
for (const bubble of bubbles) {
|
|
7309
|
+
const ts = bubbleTs(bubble, startedAt);
|
|
7310
|
+
if (!ts) {
|
|
7311
|
+
continue;
|
|
7312
|
+
}
|
|
7313
|
+
const bubbleId = stringField(bubble, "bubbleId") || `bubble_${lineNumber}`;
|
|
7314
|
+
const bubbleType = numberField(bubble, "type");
|
|
7315
|
+
const capabilityType = numberField(bubble, "capabilityType");
|
|
7316
|
+
const toolFormer = objectField(bubble, "toolFormerData");
|
|
7317
|
+
const bubbleModel = stringField(objectField(bubble, "modelInfo"), "modelName") || model;
|
|
7318
|
+
if (bubbleType === BUBBLE_USER) {
|
|
7319
|
+
if (currentTurnId && currentTurnStartedAt) {
|
|
7320
|
+
push({
|
|
7321
|
+
ts,
|
|
7322
|
+
type: "turn.completed",
|
|
7323
|
+
turnId: currentTurnId,
|
|
7324
|
+
model: bubbleModel,
|
|
7325
|
+
success: true,
|
|
7326
|
+
confidence: "derived",
|
|
7327
|
+
metrics: { durationMs: durationMsBetween(currentTurnStartedAt, ts) },
|
|
7328
|
+
refs: stringRefs({ sourceId: `${sessionId}:${currentTurnId}:completed` })
|
|
7329
|
+
}, "bubble");
|
|
7330
|
+
}
|
|
7331
|
+
turnIndex += 1;
|
|
7332
|
+
currentTurnId = `turn_${turnIndex}`;
|
|
7333
|
+
currentTurnStartedAt = ts;
|
|
7334
|
+
const text = stringField(bubble, "text") || "";
|
|
7335
|
+
push({
|
|
7336
|
+
ts,
|
|
7337
|
+
type: "turn.started",
|
|
7338
|
+
turnId: currentTurnId,
|
|
7339
|
+
model: bubbleModel,
|
|
7340
|
+
confidence: "partial",
|
|
7341
|
+
refs: stringRefs({ sourceId: `${sessionId}:${currentTurnId}:started` })
|
|
7342
|
+
}, "bubble");
|
|
7343
|
+
if (text) {
|
|
7344
|
+
push({
|
|
7345
|
+
ts,
|
|
7346
|
+
type: "prompt.submitted",
|
|
7347
|
+
turnId: currentTurnId,
|
|
7348
|
+
model: bubbleModel,
|
|
7349
|
+
confidence: "partial",
|
|
7350
|
+
metrics: { prompts: 1, promptChars: text.length },
|
|
7351
|
+
refs: stringRefs({
|
|
7352
|
+
sourceId: `${sessionId}:${cleanId(bubbleId)}:prompt`,
|
|
7353
|
+
promptHash: `sha256:${createStableHash(text)}`
|
|
7354
|
+
})
|
|
7355
|
+
}, "bubble");
|
|
7356
|
+
}
|
|
7357
|
+
continue;
|
|
7358
|
+
}
|
|
7359
|
+
if (capabilityType === CAPABILITY_TOOL && Object.keys(toolFormer).length > 0) {
|
|
7360
|
+
const rawName = stringField(toolFormer, "name") || "unknown";
|
|
7361
|
+
const tool = toolNameOf(rawName);
|
|
7362
|
+
const toolCallId = cleanId(
|
|
7363
|
+
stringField(toolFormer, "toolCallId") || `${sessionId}:${cleanId(bubbleId)}`
|
|
7364
|
+
);
|
|
7365
|
+
const status = (stringField(toolFormer, "status") || "completed").toLowerCase();
|
|
7366
|
+
const success = status !== "error" && status !== "failed" && status !== "cancelled";
|
|
7367
|
+
const input = parseMaybeJsonObject(toolFormer.rawArgs);
|
|
7368
|
+
const params = parseMaybeJsonObject(toolFormer.params);
|
|
7369
|
+
const mergedInput = { ...params, ...input };
|
|
7370
|
+
const fileActivities = fileActivitiesFromCursorTool(tool, mergedInput, ts, cwd);
|
|
7371
|
+
if (fileActivities.some((file) => (file.linesAdded || 0) > 0 || (file.linesRemoved || 0) > 0)) {
|
|
7372
|
+
hasFileLineData = true;
|
|
7373
|
+
}
|
|
7374
|
+
push({
|
|
7375
|
+
ts,
|
|
7376
|
+
type: "tool.started",
|
|
7377
|
+
turnId: currentTurnId,
|
|
7378
|
+
tool,
|
|
7379
|
+
operation: `${tool} started`,
|
|
7380
|
+
model: bubbleModel,
|
|
7381
|
+
confidence: "exact",
|
|
7382
|
+
fileActivities: fileActivities.length > 0 ? fileActivities : void 0,
|
|
7383
|
+
refs: stringRefs({ sourceId: `${toolCallId}:started` })
|
|
7384
|
+
}, "tool");
|
|
7385
|
+
push({
|
|
7386
|
+
ts,
|
|
7387
|
+
type: success ? "tool.completed" : "tool.failed",
|
|
7388
|
+
turnId: currentTurnId,
|
|
7389
|
+
tool,
|
|
7390
|
+
operation: `${tool} completed`,
|
|
7391
|
+
success,
|
|
7392
|
+
model: bubbleModel,
|
|
7393
|
+
confidence: "exact",
|
|
7394
|
+
metrics: { toolCalls: 1 },
|
|
7395
|
+
refs: stringRefs({ sourceId: `${toolCallId}:completed` })
|
|
7396
|
+
}, "tool");
|
|
7397
|
+
if (isShellTool(tool)) {
|
|
7398
|
+
const command = stringField(mergedInput, "command") || stringField(mergedInput, "cmd");
|
|
7399
|
+
if (command) {
|
|
7400
|
+
push({
|
|
7401
|
+
ts,
|
|
7402
|
+
type: "command.started",
|
|
7403
|
+
turnId: currentTurnId,
|
|
7404
|
+
tool,
|
|
7405
|
+
operation: "command started",
|
|
7406
|
+
confidence: "derived",
|
|
7407
|
+
refs: stringRefs({
|
|
7408
|
+
sourceId: `${toolCallId}:command:started`,
|
|
7409
|
+
commandHash: `sha256:${createStableHash(command)}`
|
|
7410
|
+
})
|
|
7411
|
+
}, "tool");
|
|
7412
|
+
push({
|
|
7413
|
+
ts,
|
|
7414
|
+
type: success ? "command.completed" : "command.failed",
|
|
7415
|
+
turnId: currentTurnId,
|
|
7416
|
+
tool,
|
|
7417
|
+
operation: "command completed",
|
|
7418
|
+
success,
|
|
7419
|
+
confidence: "derived",
|
|
7420
|
+
metrics: { commandCalls: 1 },
|
|
7421
|
+
refs: stringRefs({ sourceId: `${toolCallId}:command:completed` })
|
|
7422
|
+
}, "tool");
|
|
7423
|
+
}
|
|
7424
|
+
}
|
|
7425
|
+
}
|
|
7426
|
+
const metrics = usageMetrics(objectField(bubble, "tokenCount"));
|
|
7427
|
+
if (metrics) {
|
|
7428
|
+
push({
|
|
7429
|
+
ts,
|
|
7430
|
+
type: "model.usage",
|
|
7431
|
+
turnId: currentTurnId,
|
|
7432
|
+
model: bubbleModel,
|
|
7433
|
+
confidence: "partial",
|
|
7434
|
+
metrics,
|
|
7435
|
+
refs: stringRefs({ sourceId: `${sessionId}:${cleanId(bubbleId)}:usage` })
|
|
7436
|
+
}, "bubble");
|
|
7437
|
+
}
|
|
7438
|
+
}
|
|
7439
|
+
if (currentTurnId && endedAt) {
|
|
7440
|
+
push({
|
|
7441
|
+
ts: endedAt,
|
|
7442
|
+
type: "turn.completed",
|
|
7443
|
+
turnId: currentTurnId,
|
|
7444
|
+
success: true,
|
|
7445
|
+
confidence: "derived",
|
|
7446
|
+
metrics: { durationMs: durationMsBetween(currentTurnStartedAt, endedAt) },
|
|
7447
|
+
refs: stringRefs({ sourceId: `${sessionId}:${currentTurnId}:completed` })
|
|
7448
|
+
}, "bubble");
|
|
7449
|
+
}
|
|
7450
|
+
const linesAdded = numberField(data, "totalLinesAdded");
|
|
7451
|
+
const linesRemoved = numberField(data, "totalLinesRemoved");
|
|
7452
|
+
if (!hasFileLineData && endedAt && ((linesAdded || 0) > 0 || (linesRemoved || 0) > 0)) {
|
|
7453
|
+
push({
|
|
7454
|
+
ts: endedAt,
|
|
7455
|
+
type: "file.changed",
|
|
7456
|
+
confidence: "partial",
|
|
7457
|
+
metrics: {
|
|
7458
|
+
linesAdded: linesAdded || void 0,
|
|
7459
|
+
linesRemoved: linesRemoved || void 0
|
|
7460
|
+
},
|
|
7461
|
+
refs: stringRefs({ sourceId: `${sessionId}:lines` })
|
|
7462
|
+
}, "composer");
|
|
7463
|
+
}
|
|
7464
|
+
if (endedAt) {
|
|
7465
|
+
push({
|
|
7466
|
+
ts: endedAt,
|
|
7467
|
+
type: "session.ended",
|
|
7468
|
+
confidence: "derived",
|
|
7469
|
+
refs: stringRefs({ sourceId: `${sessionId}:ended` })
|
|
7470
|
+
}, "composer");
|
|
7471
|
+
}
|
|
7472
|
+
emitPersistedCursorUsage(push, persistedUsage, endedAt || startedAt, currentTurnId, model, sessionId, events.some((event) => event.type === "model.usage"));
|
|
7473
|
+
return events;
|
|
7474
|
+
}
|
|
7475
|
+
async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
|
|
7476
|
+
if (sourceRoot) {
|
|
7477
|
+
const info = await stat8(sourceRoot).catch(() => null);
|
|
7478
|
+
if (!info) {
|
|
7479
|
+
return [];
|
|
7480
|
+
}
|
|
7481
|
+
if (info.isDirectory()) {
|
|
7482
|
+
const files2 = [];
|
|
7483
|
+
const skipIds2 = /* @__PURE__ */ new Set();
|
|
7484
|
+
for (const dbPath of await listFilesByExtensions(sourceRoot, [".vscdb", ".db"])) {
|
|
7485
|
+
const base = path13.basename(dbPath);
|
|
7486
|
+
if (base === "store.db" || base !== "state.vscdb" && !base.endsWith(".vscdb")) {
|
|
7487
|
+
continue;
|
|
7488
|
+
}
|
|
7489
|
+
const dbInfo = await stat8(dbPath).catch(() => null);
|
|
7490
|
+
if (!dbInfo) {
|
|
7491
|
+
continue;
|
|
7492
|
+
}
|
|
7493
|
+
const composerIds2 = await listCursorComposerIds(dbPath);
|
|
7494
|
+
for (const id of composerIds2) {
|
|
7495
|
+
skipIds2.add(id);
|
|
7496
|
+
}
|
|
7497
|
+
files2.push({
|
|
7498
|
+
path: dbPath,
|
|
7499
|
+
modifiedAt: await modifiedAtWithHookUsage(dbInfo.mtime, home, composerIds2)
|
|
7500
|
+
});
|
|
7501
|
+
}
|
|
7502
|
+
files2.push(...await collectCursorTranscriptFiles(sourceRoot, home, skipIds2));
|
|
7503
|
+
return files2;
|
|
7504
|
+
}
|
|
7505
|
+
if (path13.basename(sourceRoot) === "store.db") {
|
|
7506
|
+
return [];
|
|
7507
|
+
}
|
|
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
|
+
}];
|
|
6179
7523
|
}
|
|
6180
|
-
|
|
6181
|
-
|
|
7524
|
+
const files = [];
|
|
7525
|
+
const skipIds = /* @__PURE__ */ new Set();
|
|
7526
|
+
for (const candidatePath of cursorStateDbCandidates(home, env)) {
|
|
7527
|
+
const info = await stat8(candidatePath).catch(() => null);
|
|
7528
|
+
if (!info) {
|
|
6182
7529
|
continue;
|
|
6183
7530
|
}
|
|
6184
|
-
const
|
|
6185
|
-
const
|
|
6186
|
-
|
|
6187
|
-
results.push({ path: eventsPath, modifiedAt: info.mtime.toISOString() });
|
|
7531
|
+
const composerIds = await listCursorComposerIds(candidatePath);
|
|
7532
|
+
for (const id of composerIds) {
|
|
7533
|
+
skipIds.add(id);
|
|
6188
7534
|
}
|
|
7535
|
+
files.push({
|
|
7536
|
+
path: candidatePath,
|
|
7537
|
+
modifiedAt: await modifiedAtWithHookUsage(info.mtime, home, composerIds)
|
|
7538
|
+
});
|
|
7539
|
+
break;
|
|
6189
7540
|
}
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
function copilotPluginContent() {
|
|
6193
|
-
return `// Agent Time plugin for GitHub Copilot CLI
|
|
6194
|
-
// Generated by vibetime.
|
|
6195
|
-
// Copilot does not support hooks \u2014 this file is a placeholder for detection.
|
|
6196
|
-
// Backfill reads session data from ~/.copilot/session-state/*/events.jsonl.
|
|
6197
|
-
`;
|
|
7541
|
+
files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), home, skipIds));
|
|
7542
|
+
return files;
|
|
6198
7543
|
}
|
|
6199
|
-
function
|
|
6200
|
-
const
|
|
6201
|
-
|
|
6202
|
-
|
|
7544
|
+
function cursorHookConfig() {
|
|
7545
|
+
const entry = { command: HOOK_COMMAND, timeout: HOOK_TIMEOUT_SECONDS };
|
|
7546
|
+
const hooks = {};
|
|
7547
|
+
for (const event of CURSOR_HOOK_EVENTS) {
|
|
7548
|
+
hooks[event] = [entry];
|
|
6203
7549
|
}
|
|
6204
|
-
return
|
|
7550
|
+
return { version: 1, hooks };
|
|
6205
7551
|
}
|
|
6206
|
-
function
|
|
7552
|
+
function createCursorAdapter() {
|
|
6207
7553
|
return {
|
|
6208
|
-
id:
|
|
6209
|
-
label: "
|
|
6210
|
-
agentName:
|
|
6211
|
-
kind: "
|
|
7554
|
+
id: SOURCE_ID,
|
|
7555
|
+
label: "Cursor",
|
|
7556
|
+
agentName: AGENT_NAME,
|
|
7557
|
+
kind: "ide",
|
|
6212
7558
|
detectPath(home, env) {
|
|
6213
|
-
return
|
|
7559
|
+
return cursorHome(home, env);
|
|
6214
7560
|
},
|
|
6215
7561
|
installedPath(home, env) {
|
|
6216
|
-
return
|
|
7562
|
+
return cursorHooksPath(home, env);
|
|
6217
7563
|
},
|
|
6218
7564
|
async isInstalled(home, env) {
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
return await pathExists2(path12.join(copilotHome(home, env), ".vibetime"));
|
|
6222
|
-
} catch {
|
|
6223
|
-
return false;
|
|
6224
|
-
}
|
|
7565
|
+
const config = await readJsonIfExists(cursorHooksPath(home, env));
|
|
7566
|
+
return hasCursorHookCommand(config, HOOK_COMMAND);
|
|
6225
7567
|
},
|
|
6226
7568
|
installEntries(home, env) {
|
|
6227
7569
|
return [{
|
|
6228
|
-
kind: "
|
|
6229
|
-
path:
|
|
6230
|
-
content:
|
|
7570
|
+
kind: "cursor-hooks-json",
|
|
7571
|
+
path: cursorHooksPath(home, env),
|
|
7572
|
+
content: cursorHookConfig()
|
|
6231
7573
|
}];
|
|
6232
7574
|
},
|
|
6233
7575
|
sourcePaths(home, env) {
|
|
6234
|
-
return [
|
|
7576
|
+
return [
|
|
7577
|
+
...cursorStateDbCandidates(home, env),
|
|
7578
|
+
cursorProjectsDir(home, env),
|
|
7579
|
+
cursorChatsDir(home, env)
|
|
7580
|
+
];
|
|
6235
7581
|
},
|
|
6236
|
-
parseSessionFile:
|
|
7582
|
+
parseSessionFile: parseCursorSessionFile
|
|
6237
7583
|
};
|
|
6238
7584
|
}
|
|
6239
7585
|
|
|
6240
7586
|
// src/adapters/grok-build.ts
|
|
6241
|
-
import { readdir as
|
|
6242
|
-
import
|
|
7587
|
+
import { readdir as readdir7, readFile as readFile9, stat as stat9 } from "node:fs/promises";
|
|
7588
|
+
import path14 from "node:path";
|
|
6243
7589
|
init_fs();
|
|
6244
7590
|
var GROK_COST_TICKS_PER_USD = 1e9;
|
|
6245
|
-
var
|
|
6246
|
-
var
|
|
6247
|
-
var
|
|
7591
|
+
var SOURCE_ID2 = "grok-build";
|
|
7592
|
+
var AGENT_NAME2 = "grok-build";
|
|
7593
|
+
var HOOK_COMMAND2 = `vibetime hook --agent ${SOURCE_ID2}`;
|
|
6248
7594
|
function grokHome(home, env) {
|
|
6249
7595
|
const override = env?.GROK_HOME;
|
|
6250
7596
|
if (override && override.trim()) {
|
|
6251
|
-
return
|
|
7597
|
+
return path14.resolve(override);
|
|
6252
7598
|
}
|
|
6253
|
-
return
|
|
7599
|
+
return path14.join(home, ".grok");
|
|
6254
7600
|
}
|
|
6255
7601
|
function grokHooksPath(home, env) {
|
|
6256
|
-
return
|
|
7602
|
+
return path14.join(grokHome(home, env), "hooks", "vibetime.json");
|
|
6257
7603
|
}
|
|
6258
7604
|
function grokSessionsDir(home, env) {
|
|
6259
|
-
return
|
|
7605
|
+
return path14.join(grokHome(home, env), "sessions");
|
|
6260
7606
|
}
|
|
6261
7607
|
async function grokBackfillFiles(sourceRoot, home, env) {
|
|
6262
7608
|
const root = sourceRoot || grokSessionsDir(home, env);
|
|
@@ -6264,12 +7610,12 @@ async function grokBackfillFiles(sourceRoot, home, env) {
|
|
|
6264
7610
|
async function walk(dir) {
|
|
6265
7611
|
let entries;
|
|
6266
7612
|
try {
|
|
6267
|
-
entries = await
|
|
7613
|
+
entries = await readdir7(dir, { withFileTypes: true });
|
|
6268
7614
|
} catch {
|
|
6269
7615
|
return;
|
|
6270
7616
|
}
|
|
6271
7617
|
for (const entry of entries) {
|
|
6272
|
-
const entryPath =
|
|
7618
|
+
const entryPath = path14.join(dir, entry.name);
|
|
6273
7619
|
if (entry.isDirectory()) {
|
|
6274
7620
|
await walk(entryPath);
|
|
6275
7621
|
continue;
|
|
@@ -6278,7 +7624,7 @@ async function grokBackfillFiles(sourceRoot, home, env) {
|
|
|
6278
7624
|
continue;
|
|
6279
7625
|
}
|
|
6280
7626
|
try {
|
|
6281
|
-
const info = await
|
|
7627
|
+
const info = await stat9(entryPath);
|
|
6282
7628
|
files.push({ path: entryPath, modifiedAt: info.mtime.toISOString() });
|
|
6283
7629
|
} catch {
|
|
6284
7630
|
}
|
|
@@ -6292,18 +7638,18 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6292
7638
|
if (!sessionDir) {
|
|
6293
7639
|
return [];
|
|
6294
7640
|
}
|
|
6295
|
-
const summaryPath =
|
|
6296
|
-
const eventsPath =
|
|
6297
|
-
const signalsPath =
|
|
6298
|
-
const updatesPath =
|
|
7641
|
+
const summaryPath = path14.join(sessionDir, "summary.json");
|
|
7642
|
+
const eventsPath = path14.join(sessionDir, "events.jsonl");
|
|
7643
|
+
const signalsPath = path14.join(sessionDir, "signals.json");
|
|
7644
|
+
const updatesPath = path14.join(sessionDir, "updates.jsonl");
|
|
6299
7645
|
const summary = await readJsonIfExists(summaryPath);
|
|
6300
7646
|
if (!isPlainObject(summary)) {
|
|
6301
7647
|
return [];
|
|
6302
7648
|
}
|
|
6303
7649
|
const info = objectField(summary, "info");
|
|
6304
|
-
const sessionId = stringField(info, "id") ||
|
|
7650
|
+
const sessionId = stringField(info, "id") || path14.basename(sessionDir);
|
|
6305
7651
|
const cwd = stringField(info, "cwd");
|
|
6306
|
-
const project = cwd ?
|
|
7652
|
+
const project = cwd ? path14.basename(cwd) : void 0;
|
|
6307
7653
|
const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
|
|
6308
7654
|
const model = stringField(summary, "current_model_id");
|
|
6309
7655
|
const createdAt = timestampFrom(summary.created_at) || timestampFrom(summary.createdAt);
|
|
@@ -6330,8 +7676,8 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6330
7676
|
};
|
|
6331
7677
|
const base = (partial) => ({
|
|
6332
7678
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6333
|
-
source:
|
|
6334
|
-
agent:
|
|
7679
|
+
source: SOURCE_ID2,
|
|
7680
|
+
agent: AGENT_NAME2,
|
|
6335
7681
|
workspaceId,
|
|
6336
7682
|
project,
|
|
6337
7683
|
cwd,
|
|
@@ -6351,7 +7697,7 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6351
7697
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
6352
7698
|
let eventsText = "";
|
|
6353
7699
|
try {
|
|
6354
|
-
eventsText = await
|
|
7700
|
+
eventsText = await readFile9(eventsPath, "utf8");
|
|
6355
7701
|
} catch {
|
|
6356
7702
|
}
|
|
6357
7703
|
const lines = eventsText.split("\n").filter(Boolean);
|
|
@@ -6429,7 +7775,7 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6429
7775
|
fileActivities: fileActivities.length > 0 ? fileActivities : void 0,
|
|
6430
7776
|
refs: stringRefs({ sourceId: `${toolId}:started` })
|
|
6431
7777
|
}), lineNumber, type, "tool.started");
|
|
6432
|
-
if (
|
|
7778
|
+
if (isShellTool2(toolName)) {
|
|
6433
7779
|
const command = stringField(knownInput?.input || {}, "command");
|
|
6434
7780
|
if (command) {
|
|
6435
7781
|
push(base({
|
|
@@ -6470,7 +7816,7 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6470
7816
|
},
|
|
6471
7817
|
refs: stringRefs({ sourceId: `${toolId}:completed` })
|
|
6472
7818
|
}), lineNumber, type, "tool.completed");
|
|
6473
|
-
if (
|
|
7819
|
+
if (isShellTool2(pending?.name || toolName)) {
|
|
6474
7820
|
push(base({
|
|
6475
7821
|
ts,
|
|
6476
7822
|
type: success ? "command.completed" : "command.failed",
|
|
@@ -6527,7 +7873,7 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6527
7873
|
nextLine += 1;
|
|
6528
7874
|
}
|
|
6529
7875
|
}
|
|
6530
|
-
const hunkPath =
|
|
7876
|
+
const hunkPath = path14.join(sessionDir, "hunk_records.jsonl");
|
|
6531
7877
|
const hunkActivities = await loadHunkFileActivities(hunkPath, cwd);
|
|
6532
7878
|
if (hunkActivities.length > 0) {
|
|
6533
7879
|
for (const event of events) {
|
|
@@ -6591,9 +7937,9 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6591
7937
|
return events;
|
|
6592
7938
|
}
|
|
6593
7939
|
function resolveSessionDir(filePath) {
|
|
6594
|
-
const base =
|
|
7940
|
+
const base = path14.basename(filePath);
|
|
6595
7941
|
if (base === "summary.json" || base === "events.jsonl" || base === "signals.json") {
|
|
6596
|
-
return
|
|
7942
|
+
return path14.dirname(filePath);
|
|
6597
7943
|
}
|
|
6598
7944
|
if (/^[0-9a-f]{8}-[0-9a-f-]{27,}$/i.test(base)) {
|
|
6599
7945
|
return filePath;
|
|
@@ -6616,7 +7962,7 @@ function findPendingToolId(pending, toolName) {
|
|
|
6616
7962
|
}
|
|
6617
7963
|
return void 0;
|
|
6618
7964
|
}
|
|
6619
|
-
function
|
|
7965
|
+
function isShellTool2(name) {
|
|
6620
7966
|
if (!name) {
|
|
6621
7967
|
return false;
|
|
6622
7968
|
}
|
|
@@ -6625,7 +7971,7 @@ function isShellTool(name) {
|
|
|
6625
7971
|
}
|
|
6626
7972
|
function fileActivitiesFromTool(toolName, input, ts, cwd) {
|
|
6627
7973
|
const lower = toolName.toLowerCase();
|
|
6628
|
-
if (
|
|
7974
|
+
if (isShellTool2(toolName)) {
|
|
6629
7975
|
const command = stringField(input, "command");
|
|
6630
7976
|
if (command) {
|
|
6631
7977
|
return fileActivitiesFromShellCommand(command, ts, cwd, cwd);
|
|
@@ -6679,17 +8025,17 @@ function fileActivitiesFromTool(toolName, input, ts, cwd) {
|
|
|
6679
8025
|
return [];
|
|
6680
8026
|
}
|
|
6681
8027
|
function displayPath(filePath, cwd) {
|
|
6682
|
-
if (!cwd || !
|
|
8028
|
+
if (!cwd || !path14.isAbsolute(filePath)) {
|
|
6683
8029
|
return filePath;
|
|
6684
8030
|
}
|
|
6685
|
-
const relative =
|
|
6686
|
-
return relative && !relative.startsWith("..") && !
|
|
8031
|
+
const relative = path14.relative(cwd, filePath);
|
|
8032
|
+
return relative && !relative.startsWith("..") && !path14.isAbsolute(relative) ? relative : filePath;
|
|
6687
8033
|
}
|
|
6688
8034
|
async function loadToolInputsFromUpdates(updatesPath) {
|
|
6689
8035
|
const map = /* @__PURE__ */ new Map();
|
|
6690
8036
|
let text = "";
|
|
6691
8037
|
try {
|
|
6692
|
-
text = await
|
|
8038
|
+
text = await readFile9(updatesPath, "utf8");
|
|
6693
8039
|
} catch {
|
|
6694
8040
|
return map;
|
|
6695
8041
|
}
|
|
@@ -6760,7 +8106,7 @@ async function loadTurnUsagesFromUpdates(updatesPath) {
|
|
|
6760
8106
|
const turns = [];
|
|
6761
8107
|
let text = "";
|
|
6762
8108
|
try {
|
|
6763
|
-
text = await
|
|
8109
|
+
text = await readFile9(updatesPath, "utf8");
|
|
6764
8110
|
} catch {
|
|
6765
8111
|
return turns;
|
|
6766
8112
|
}
|
|
@@ -6849,7 +8195,7 @@ async function loadHunkFileActivities(hunkPath, cwd) {
|
|
|
6849
8195
|
const activities = [];
|
|
6850
8196
|
let text = "";
|
|
6851
8197
|
try {
|
|
6852
|
-
text = await
|
|
8198
|
+
text = await readFile9(hunkPath, "utf8");
|
|
6853
8199
|
} catch {
|
|
6854
8200
|
return activities;
|
|
6855
8201
|
}
|
|
@@ -6891,7 +8237,7 @@ async function loadHunkFileActivities(hunkPath, cwd) {
|
|
|
6891
8237
|
}
|
|
6892
8238
|
return activities;
|
|
6893
8239
|
}
|
|
6894
|
-
var grokHandler = (msg) => hookHandler(
|
|
8240
|
+
var grokHandler = (msg) => hookHandler(SOURCE_ID2, msg);
|
|
6895
8241
|
function hookConfig5() {
|
|
6896
8242
|
const anyTool = [{ matcher: ".*", hooks: [grokHandler("Reporting tool activity")] }];
|
|
6897
8243
|
return {
|
|
@@ -6913,9 +8259,9 @@ function hookConfig5() {
|
|
|
6913
8259
|
}
|
|
6914
8260
|
function createGrokBuildAdapter() {
|
|
6915
8261
|
return {
|
|
6916
|
-
id:
|
|
8262
|
+
id: SOURCE_ID2,
|
|
6917
8263
|
label: "Grok Build",
|
|
6918
|
-
agentName:
|
|
8264
|
+
agentName: AGENT_NAME2,
|
|
6919
8265
|
kind: "agent",
|
|
6920
8266
|
detectPath(home, env) {
|
|
6921
8267
|
return grokHome(home, env);
|
|
@@ -6924,7 +8270,7 @@ function createGrokBuildAdapter() {
|
|
|
6924
8270
|
return grokHooksPath(home, env);
|
|
6925
8271
|
},
|
|
6926
8272
|
async isInstalled(home, env) {
|
|
6927
|
-
return isHooksJsonInstalled(grokHooksPath(home, env),
|
|
8273
|
+
return isHooksJsonInstalled(grokHooksPath(home, env), HOOK_COMMAND2);
|
|
6928
8274
|
},
|
|
6929
8275
|
installEntries(home, env) {
|
|
6930
8276
|
return [{
|
|
@@ -6941,8 +8287,8 @@ function createGrokBuildAdapter() {
|
|
|
6941
8287
|
}
|
|
6942
8288
|
|
|
6943
8289
|
// src/adapters/kimi-code.ts
|
|
6944
|
-
import { readFile as
|
|
6945
|
-
import
|
|
8290
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
8291
|
+
import path15 from "node:path";
|
|
6946
8292
|
|
|
6947
8293
|
// src/lib/toml-hooks.ts
|
|
6948
8294
|
init_fs();
|
|
@@ -7089,20 +8435,20 @@ function tomlNumberField(body, key) {
|
|
|
7089
8435
|
}
|
|
7090
8436
|
|
|
7091
8437
|
// src/adapters/kimi-code.ts
|
|
7092
|
-
var
|
|
7093
|
-
var
|
|
8438
|
+
var HOOK_COMMAND3 = "vibetime hook --agent kimi-code";
|
|
8439
|
+
var HOOK_TIMEOUT_SECONDS2 = 10;
|
|
7094
8440
|
function kimiCodeHome(home, env) {
|
|
7095
8441
|
const override = env?.KIMI_CODE_HOME || env?.KIMI_HOME;
|
|
7096
8442
|
if (override && override.trim()) {
|
|
7097
|
-
return
|
|
8443
|
+
return path15.resolve(override);
|
|
7098
8444
|
}
|
|
7099
|
-
return
|
|
8445
|
+
return path15.join(home, ".kimi-code");
|
|
7100
8446
|
}
|
|
7101
8447
|
function kimiCodeSessionsDir(home, env) {
|
|
7102
|
-
return
|
|
8448
|
+
return path15.join(kimiCodeHome(home, env), "sessions");
|
|
7103
8449
|
}
|
|
7104
8450
|
function kimiCodeConfigPath(home, env) {
|
|
7105
|
-
return
|
|
8451
|
+
return path15.join(kimiCodeHome(home, env), "config.toml");
|
|
7106
8452
|
}
|
|
7107
8453
|
function kimiHookRules() {
|
|
7108
8454
|
const events = [
|
|
@@ -7119,8 +8465,8 @@ function kimiHookRules() {
|
|
|
7119
8465
|
];
|
|
7120
8466
|
return events.map((event) => ({
|
|
7121
8467
|
event,
|
|
7122
|
-
command:
|
|
7123
|
-
timeout:
|
|
8468
|
+
command: HOOK_COMMAND3,
|
|
8469
|
+
timeout: HOOK_TIMEOUT_SECONDS2
|
|
7124
8470
|
}));
|
|
7125
8471
|
}
|
|
7126
8472
|
function baseKimiEvent(event) {
|
|
@@ -7179,10 +8525,10 @@ function sessionIdFromWirePath(filePath) {
|
|
|
7179
8525
|
return match?.[1];
|
|
7180
8526
|
}
|
|
7181
8527
|
async function readSessionState(filePath) {
|
|
7182
|
-
const sessionDir =
|
|
7183
|
-
const statePath =
|
|
8528
|
+
const sessionDir = path15.dirname(path15.dirname(path15.dirname(filePath)));
|
|
8529
|
+
const statePath = path15.join(sessionDir, "state.json");
|
|
7184
8530
|
try {
|
|
7185
|
-
const text = await
|
|
8531
|
+
const text = await readFile10(statePath, "utf8");
|
|
7186
8532
|
const raw = JSON.parse(text);
|
|
7187
8533
|
if (!isPlainObject(raw)) {
|
|
7188
8534
|
return {};
|
|
@@ -7197,15 +8543,15 @@ async function readSessionState(filePath) {
|
|
|
7197
8543
|
}
|
|
7198
8544
|
}
|
|
7199
8545
|
async function parseKimiCodeSessionFile(filePath, options) {
|
|
7200
|
-
if (
|
|
8546
|
+
if (path15.basename(filePath) !== "wire.jsonl") {
|
|
7201
8547
|
return [];
|
|
7202
8548
|
}
|
|
7203
|
-
const text = await
|
|
8549
|
+
const text = await readFile10(filePath, "utf8");
|
|
7204
8550
|
const lines = text.split("\n").filter(Boolean);
|
|
7205
8551
|
const stateMeta = await readSessionState(filePath);
|
|
7206
8552
|
let sessionId = stateMeta.sessionId || sessionIdFromWirePath(filePath);
|
|
7207
8553
|
let cwd = stateMeta.cwd;
|
|
7208
|
-
let project = cwd ?
|
|
8554
|
+
let project = cwd ? path15.basename(cwd) : void 0;
|
|
7209
8555
|
let model;
|
|
7210
8556
|
let provider;
|
|
7211
8557
|
let reasoningEffort;
|
|
@@ -7251,7 +8597,7 @@ async function parseKimiCodeSessionFile(filePath, options) {
|
|
|
7251
8597
|
const disclosedCwd = stringField(disclosure, "cwd");
|
|
7252
8598
|
if (disclosedCwd) {
|
|
7253
8599
|
cwd = disclosedCwd;
|
|
7254
|
-
project =
|
|
8600
|
+
project = path15.basename(disclosedCwd);
|
|
7255
8601
|
}
|
|
7256
8602
|
continue;
|
|
7257
8603
|
}
|
|
@@ -7565,7 +8911,7 @@ function createKimiCodeAdapter() {
|
|
|
7565
8911
|
return kimiCodeConfigPath(home, env);
|
|
7566
8912
|
},
|
|
7567
8913
|
async isInstalled(home, env) {
|
|
7568
|
-
return hasTomlHookCommand(kimiCodeConfigPath(home, env),
|
|
8914
|
+
return hasTomlHookCommand(kimiCodeConfigPath(home, env), HOOK_COMMAND3);
|
|
7569
8915
|
},
|
|
7570
8916
|
installEntries(home, env) {
|
|
7571
8917
|
return [{
|
|
@@ -7582,8 +8928,8 @@ function createKimiCodeAdapter() {
|
|
|
7582
8928
|
}
|
|
7583
8929
|
|
|
7584
8930
|
// src/adapters/opencode.ts
|
|
7585
|
-
import
|
|
7586
|
-
import
|
|
8931
|
+
import os7 from "node:os";
|
|
8932
|
+
import path16 from "node:path";
|
|
7587
8933
|
async function parseOpenCodeSessionFile(dbPath, options) {
|
|
7588
8934
|
const { DatabaseSync } = await import("node:sqlite");
|
|
7589
8935
|
if (!dbPath.endsWith(".db")) {
|
|
@@ -7642,7 +8988,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
|
|
|
7642
8988
|
const isSubagent = sessionId !== rawSessionId;
|
|
7643
8989
|
const sessionEventStart = events.length;
|
|
7644
8990
|
const cwd = session.directory || session.path || void 0;
|
|
7645
|
-
const project = cwd ?
|
|
8991
|
+
const project = cwd ? path16.basename(cwd) : void 0;
|
|
7646
8992
|
const sessionTs = msToIso(session.time_created);
|
|
7647
8993
|
events.push(baseOpenCodeEvent({
|
|
7648
8994
|
ts: sessionTs,
|
|
@@ -7750,7 +9096,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
|
|
|
7750
9096
|
const provider = currentProvider;
|
|
7751
9097
|
const pathObj = objectField(info, "path");
|
|
7752
9098
|
const assistantCwd = stringField(pathObj, "cwd") || cwd;
|
|
7753
|
-
const assistantProject = assistantCwd ?
|
|
9099
|
+
const assistantProject = assistantCwd ? path16.basename(assistantCwd) : project;
|
|
7754
9100
|
const completedTs = numberField(objectField(info, "time"), "completed");
|
|
7755
9101
|
const createdTs = timeCreated;
|
|
7756
9102
|
const tokens = opencodeUsageFromInfo(info);
|
|
@@ -8024,33 +9370,33 @@ function opencodeUsageFromInfo(info) {
|
|
|
8024
9370
|
function opencodeConfigDir(home, env) {
|
|
8025
9371
|
const override = env?.OPENCODE_CONFIG_DIR;
|
|
8026
9372
|
if (override && override.trim()) {
|
|
8027
|
-
return
|
|
9373
|
+
return path16.resolve(override);
|
|
8028
9374
|
}
|
|
8029
9375
|
const xdgConfig = env?.XDG_CONFIG_HOME;
|
|
8030
9376
|
if (xdgConfig && xdgConfig.trim()) {
|
|
8031
|
-
return
|
|
9377
|
+
return path16.join(path16.resolve(xdgConfig), "opencode");
|
|
8032
9378
|
}
|
|
8033
|
-
return
|
|
9379
|
+
return path16.join(home, ".config", "opencode");
|
|
8034
9380
|
}
|
|
8035
9381
|
function opencodeDataCandidates(home, env) {
|
|
8036
9382
|
const xdgData = env?.XDG_DATA_HOME;
|
|
8037
|
-
const primary = xdgData && xdgData.trim() ?
|
|
8038
|
-
return [primary,
|
|
9383
|
+
const primary = xdgData && xdgData.trim() ? path16.join(path16.resolve(xdgData), "opencode", "opencode.db") : path16.join(home, ".local", "share", "opencode", "opencode.db");
|
|
9384
|
+
return [primary, path16.join(home, ".opencode", "opencode.db")];
|
|
8039
9385
|
}
|
|
8040
|
-
async function opencodeBackfillFiles(sourceRoot, home =
|
|
8041
|
-
const { stat:
|
|
9386
|
+
async function opencodeBackfillFiles(sourceRoot, home = os7.homedir(), env) {
|
|
9387
|
+
const { stat: stat17 } = await import("node:fs/promises");
|
|
8042
9388
|
if (sourceRoot) {
|
|
8043
9389
|
if (!sourceRoot.endsWith(".db")) {
|
|
8044
9390
|
return [];
|
|
8045
9391
|
}
|
|
8046
|
-
const info = await
|
|
9392
|
+
const info = await stat17(sourceRoot).catch(() => null);
|
|
8047
9393
|
if (!info) {
|
|
8048
9394
|
return [];
|
|
8049
9395
|
}
|
|
8050
9396
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
8051
9397
|
}
|
|
8052
9398
|
for (const candidatePath of opencodeDataCandidates(home, env)) {
|
|
8053
|
-
const info = await
|
|
9399
|
+
const info = await stat17(candidatePath).catch(() => null);
|
|
8054
9400
|
if (info) {
|
|
8055
9401
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
8056
9402
|
}
|
|
@@ -8125,12 +9471,12 @@ function createOpenCodeAdapter() {
|
|
|
8125
9471
|
return opencodeConfigDir(home, env);
|
|
8126
9472
|
},
|
|
8127
9473
|
installedPath(home, env) {
|
|
8128
|
-
return
|
|
9474
|
+
return path16.join(opencodeConfigDir(home, env), PLUGIN_PATH);
|
|
8129
9475
|
},
|
|
8130
9476
|
async isInstalled(home, env) {
|
|
8131
9477
|
try {
|
|
8132
9478
|
const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
8133
|
-
return await pathExists2(
|
|
9479
|
+
return await pathExists2(path16.join(opencodeConfigDir(home, env), PLUGIN_PATH)) || await pathExists2(path16.join(".opencode", PLUGIN_PATH));
|
|
8134
9480
|
} catch {
|
|
8135
9481
|
return false;
|
|
8136
9482
|
}
|
|
@@ -8138,7 +9484,7 @@ function createOpenCodeAdapter() {
|
|
|
8138
9484
|
installEntries(home, env) {
|
|
8139
9485
|
return [{
|
|
8140
9486
|
kind: "file",
|
|
8141
|
-
path:
|
|
9487
|
+
path: path16.join(opencodeConfigDir(home, env), PLUGIN_PATH),
|
|
8142
9488
|
content: opencodePluginContent()
|
|
8143
9489
|
}];
|
|
8144
9490
|
},
|
|
@@ -8150,9 +9496,9 @@ function createOpenCodeAdapter() {
|
|
|
8150
9496
|
}
|
|
8151
9497
|
|
|
8152
9498
|
// src/adapters/pi.ts
|
|
8153
|
-
import { readdir as
|
|
8154
|
-
import
|
|
8155
|
-
import
|
|
9499
|
+
import { readdir as readdir8, readFile as readFile11, stat as stat10 } from "node:fs/promises";
|
|
9500
|
+
import os8 from "node:os";
|
|
9501
|
+
import path17 from "node:path";
|
|
8156
9502
|
init_fs();
|
|
8157
9503
|
function piHostSessionIdFromBasename(basename) {
|
|
8158
9504
|
const withoutExt = basename.endsWith(".jsonl") ? basename.slice(0, -".jsonl".length) : basename;
|
|
@@ -8167,7 +9513,7 @@ function piHostSessionIdFromBasename(basename) {
|
|
|
8167
9513
|
}
|
|
8168
9514
|
function resolvePiBackfillGroupId(filePath) {
|
|
8169
9515
|
const normalized = filePath.replaceAll("\\", "/");
|
|
8170
|
-
const base =
|
|
9516
|
+
const base = path17.basename(filePath);
|
|
8171
9517
|
const nest = normalized.match(/([^/]+)\/[^/]+\/run-\d+\/session\.jsonl$/);
|
|
8172
9518
|
if (nest) {
|
|
8173
9519
|
return piHostSessionIdFromBasename(nest[1]);
|
|
@@ -8179,8 +9525,8 @@ function resolvePiBackfillGroupId(filePath) {
|
|
|
8179
9525
|
}
|
|
8180
9526
|
function parsePiSubagentLink(filePath, headerParentSession) {
|
|
8181
9527
|
if (headerParentSession) {
|
|
8182
|
-
const parentFile =
|
|
8183
|
-
const parentSessionId2 = parentFile ?
|
|
9528
|
+
const parentFile = path17.isAbsolute(headerParentSession) ? headerParentSession : void 0;
|
|
9529
|
+
const parentSessionId2 = parentFile ? path17.basename(parentFile, ".jsonl") : headerParentSession;
|
|
8184
9530
|
return { parentSessionId: parentSessionId2, parentSessionFile: parentFile, explicit: true };
|
|
8185
9531
|
}
|
|
8186
9532
|
const normalized = filePath.replaceAll("\\", "/");
|
|
@@ -8193,20 +9539,20 @@ function parsePiSubagentLink(filePath, headerParentSession) {
|
|
|
8193
9539
|
return void 0;
|
|
8194
9540
|
}
|
|
8195
9541
|
const parentSessionId = parentBasename.endsWith(".jsonl") ? parentBasename.slice(0, -".jsonl".length) : parentBasename;
|
|
8196
|
-
const parentDir =
|
|
8197
|
-
const parentSessionFile =
|
|
9542
|
+
const parentDir = path17.dirname(path17.dirname(path17.dirname(filePath)));
|
|
9543
|
+
const parentSessionFile = path17.join(path17.dirname(parentDir), `${parentBasename}.jsonl`);
|
|
8198
9544
|
return { parentSessionId, parentSessionFile, explicit: false };
|
|
8199
9545
|
}
|
|
8200
9546
|
async function resolveParentContext(link, options) {
|
|
8201
9547
|
if (link.parentSessionFile) {
|
|
8202
9548
|
try {
|
|
8203
|
-
const text = await
|
|
9549
|
+
const text = await readFile11(link.parentSessionFile, "utf8");
|
|
8204
9550
|
const firstLine = text.split("\n").find((line) => line.trim().length > 0);
|
|
8205
9551
|
const raw = firstLine ? parseJsonLine(firstLine) : void 0;
|
|
8206
9552
|
if (raw) {
|
|
8207
9553
|
const id = stringField(raw, "id");
|
|
8208
9554
|
const cwd = stringField(raw, "cwd");
|
|
8209
|
-
const project = cwd ?
|
|
9555
|
+
const project = cwd ? path17.basename(cwd) : void 0;
|
|
8210
9556
|
if (id || cwd || project) {
|
|
8211
9557
|
return { sessionId: id, cwd, project };
|
|
8212
9558
|
}
|
|
@@ -8255,7 +9601,7 @@ function rebuildEventIdentity2(event) {
|
|
|
8255
9601
|
};
|
|
8256
9602
|
}
|
|
8257
9603
|
async function parsePiSessionFile(filePath, options) {
|
|
8258
|
-
const text = await
|
|
9604
|
+
const text = await readFile11(filePath, "utf8");
|
|
8259
9605
|
const lines = text.split("\n").filter(Boolean);
|
|
8260
9606
|
let sessionId;
|
|
8261
9607
|
let cwd;
|
|
@@ -8287,7 +9633,7 @@ async function parsePiSessionFile(filePath, options) {
|
|
|
8287
9633
|
sessionId = stringField(raw, "id") || state.sessionId;
|
|
8288
9634
|
state.sessionId = sessionId || state.sessionId;
|
|
8289
9635
|
cwd = stringField(raw, "cwd") || cwd;
|
|
8290
|
-
project = cwd ?
|
|
9636
|
+
project = cwd ? path17.basename(cwd) : project;
|
|
8291
9637
|
headerParentSession = stringField(raw, "parentSession") || headerParentSession;
|
|
8292
9638
|
continue;
|
|
8293
9639
|
}
|
|
@@ -8494,7 +9840,7 @@ async function parsePiFile(filePath, options) {
|
|
|
8494
9840
|
async function parsePiWorkflowRunFile(filePath, options) {
|
|
8495
9841
|
let raw;
|
|
8496
9842
|
try {
|
|
8497
|
-
raw = JSON.parse(await
|
|
9843
|
+
raw = JSON.parse(await readFile11(filePath, "utf8"));
|
|
8498
9844
|
} catch {
|
|
8499
9845
|
return [];
|
|
8500
9846
|
}
|
|
@@ -8562,17 +9908,17 @@ async function parsePiWorkflowRunFile(filePath, options) {
|
|
|
8562
9908
|
return state.events.filter((event) => matchesBackfillFilters(event, options));
|
|
8563
9909
|
}
|
|
8564
9910
|
async function findPiSessionFileById(sessionId, options) {
|
|
8565
|
-
const sessionsDir = piSessionDir(
|
|
9911
|
+
const sessionsDir = piSessionDir(path17.resolve(stringOption(options.home) || os8.homedir()));
|
|
8566
9912
|
try {
|
|
8567
|
-
const projectDirs = await
|
|
9913
|
+
const projectDirs = await readdir8(sessionsDir, { withFileTypes: true });
|
|
8568
9914
|
for (const dir of projectDirs) {
|
|
8569
9915
|
if (!dir.isDirectory()) {
|
|
8570
9916
|
continue;
|
|
8571
9917
|
}
|
|
8572
|
-
const names = await
|
|
9918
|
+
const names = await readdir8(path17.join(sessionsDir, dir.name));
|
|
8573
9919
|
const hit = names.find((name) => name.endsWith(`_${sessionId}.jsonl`));
|
|
8574
9920
|
if (hit) {
|
|
8575
|
-
return
|
|
9921
|
+
return path17.join(sessionsDir, dir.name, hit);
|
|
8576
9922
|
}
|
|
8577
9923
|
}
|
|
8578
9924
|
} catch {
|
|
@@ -8814,23 +10160,23 @@ export default function (pi: ExtensionAPI) {
|
|
|
8814
10160
|
function piAgentDir(home, env) {
|
|
8815
10161
|
const override = env?.PI_CODING_AGENT_DIR;
|
|
8816
10162
|
if (override && override.trim()) {
|
|
8817
|
-
return
|
|
10163
|
+
return path17.resolve(override);
|
|
8818
10164
|
}
|
|
8819
|
-
return
|
|
10165
|
+
return path17.join(home, ".pi", "agent");
|
|
8820
10166
|
}
|
|
8821
10167
|
function piSessionDir(home, env) {
|
|
8822
10168
|
const override = env?.PI_CODING_AGENT_SESSION_DIR;
|
|
8823
10169
|
if (override && override.trim()) {
|
|
8824
|
-
return
|
|
10170
|
+
return path17.resolve(override);
|
|
8825
10171
|
}
|
|
8826
|
-
return
|
|
10172
|
+
return path17.join(piAgentDir(home, env), "sessions");
|
|
8827
10173
|
}
|
|
8828
10174
|
function piWorkflowProjectsDir(home) {
|
|
8829
|
-
return
|
|
10175
|
+
return path17.join(home, ".pi", "workflows", "projects");
|
|
8830
10176
|
}
|
|
8831
10177
|
async function workflowRunGroupId(filePath) {
|
|
8832
10178
|
try {
|
|
8833
|
-
const raw = JSON.parse(await
|
|
10179
|
+
const raw = JSON.parse(await readFile11(filePath, "utf8"));
|
|
8834
10180
|
if (!isPlainObject(raw)) {
|
|
8835
10181
|
return void 0;
|
|
8836
10182
|
}
|
|
@@ -8839,14 +10185,14 @@ async function workflowRunGroupId(filePath) {
|
|
|
8839
10185
|
return void 0;
|
|
8840
10186
|
}
|
|
8841
10187
|
}
|
|
8842
|
-
async function piBackfillFiles(sourceRoot, home =
|
|
10188
|
+
async function piBackfillFiles(sourceRoot, home = os8.homedir(), env) {
|
|
8843
10189
|
const lists = sourceRoot ? [await listFilesByExtensions(sourceRoot, [".jsonl", ".json"])] : await Promise.all([
|
|
8844
10190
|
listFilesByExtensions(piSessionDir(home, env), [".jsonl"]),
|
|
8845
10191
|
listFilesByExtensions(piWorkflowProjectsDir(home), [".json"])
|
|
8846
10192
|
]);
|
|
8847
10193
|
const files = lists.flat().sort();
|
|
8848
10194
|
return Promise.all(files.map(async (filePath) => {
|
|
8849
|
-
const info = await
|
|
10195
|
+
const info = await stat10(filePath);
|
|
8850
10196
|
let groupId = resolvePiBackfillGroupId(filePath);
|
|
8851
10197
|
if (!groupId && filePath.endsWith(".json")) {
|
|
8852
10198
|
groupId = await workflowRunGroupId(filePath);
|
|
@@ -8868,12 +10214,12 @@ function createPiAdapter() {
|
|
|
8868
10214
|
return piAgentDir(home, env);
|
|
8869
10215
|
},
|
|
8870
10216
|
installedPath(home, env) {
|
|
8871
|
-
return
|
|
10217
|
+
return path17.join(piAgentDir(home, env), "extensions", "vibetime.ts");
|
|
8872
10218
|
},
|
|
8873
10219
|
async isInstalled(home, env) {
|
|
8874
10220
|
try {
|
|
8875
10221
|
const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
8876
|
-
return await pathExists2(
|
|
10222
|
+
return await pathExists2(path17.join(piAgentDir(home, env), "extensions", "vibetime.ts"));
|
|
8877
10223
|
} catch {
|
|
8878
10224
|
return false;
|
|
8879
10225
|
}
|
|
@@ -8881,7 +10227,7 @@ function createPiAdapter() {
|
|
|
8881
10227
|
installEntries(home, env) {
|
|
8882
10228
|
return [{
|
|
8883
10229
|
kind: "file",
|
|
8884
|
-
path:
|
|
10230
|
+
path: path17.join(piAgentDir(home, env), "extensions", "vibetime.ts"),
|
|
8885
10231
|
content: piExtensionContent()
|
|
8886
10232
|
}];
|
|
8887
10233
|
},
|
|
@@ -8893,14 +10239,14 @@ function createPiAdapter() {
|
|
|
8893
10239
|
}
|
|
8894
10240
|
|
|
8895
10241
|
// src/adapters/qoder-cn.ts
|
|
8896
|
-
import { readdir as
|
|
8897
|
-
import
|
|
8898
|
-
import
|
|
10242
|
+
import { readdir as readdir9, readFile as readFile12, stat as stat11 } from "node:fs/promises";
|
|
10243
|
+
import os10 from "node:os";
|
|
10244
|
+
import path19 from "node:path";
|
|
8899
10245
|
|
|
8900
10246
|
// src/adapters/qoder-local-db.ts
|
|
8901
10247
|
import { access } from "node:fs/promises";
|
|
8902
|
-
import
|
|
8903
|
-
import
|
|
10248
|
+
import os9 from "node:os";
|
|
10249
|
+
import path18 from "node:path";
|
|
8904
10250
|
function takeQoderDbModelCall(calls, requestId, blockStart) {
|
|
8905
10251
|
if (requestId) {
|
|
8906
10252
|
const call = calls.byRequestId.get(requestId)?.shift();
|
|
@@ -8917,14 +10263,14 @@ function takeQoderDbModelCall(calls, requestId, blockStart) {
|
|
|
8917
10263
|
}
|
|
8918
10264
|
return calls.ordered.shift();
|
|
8919
10265
|
}
|
|
8920
|
-
function appDataRoot(appDirName, home =
|
|
10266
|
+
function appDataRoot(appDirName, home = os9.homedir()) {
|
|
8921
10267
|
if (process.platform === "darwin") {
|
|
8922
|
-
return
|
|
10268
|
+
return path18.join(home, "Library", "Application Support", appDirName);
|
|
8923
10269
|
}
|
|
8924
10270
|
if (process.platform === "win32") {
|
|
8925
|
-
return
|
|
10271
|
+
return path18.join(process.env.APPDATA || path18.join(home, "AppData", "Roaming"), appDirName);
|
|
8926
10272
|
}
|
|
8927
|
-
return
|
|
10273
|
+
return path18.join(home, ".config", appDirName);
|
|
8928
10274
|
}
|
|
8929
10275
|
function qoderLocalDbCandidates(appDirName) {
|
|
8930
10276
|
const candidates = [];
|
|
@@ -8934,8 +10280,8 @@ function qoderLocalDbCandidates(appDirName) {
|
|
|
8934
10280
|
}
|
|
8935
10281
|
const configRoot = appDataRoot(appDirName);
|
|
8936
10282
|
candidates.push(
|
|
8937
|
-
|
|
8938
|
-
|
|
10283
|
+
path18.join(configRoot, "SharedClientCache", "cache", "db", "local.db"),
|
|
10284
|
+
path18.join(configRoot, "SharedClientCache", "db", "local.db")
|
|
8939
10285
|
);
|
|
8940
10286
|
return candidates;
|
|
8941
10287
|
}
|
|
@@ -8944,7 +10290,7 @@ async function loadQoderIdeModelCatalog(appDirName, home) {
|
|
|
8944
10290
|
try {
|
|
8945
10291
|
const { DatabaseSync } = await import("node:sqlite");
|
|
8946
10292
|
const db = new DatabaseSync(
|
|
8947
|
-
|
|
10293
|
+
path18.join(appDataRoot(appDirName, home), "User", "globalStorage", "state.vscdb"),
|
|
8948
10294
|
{ readOnly: true }
|
|
8949
10295
|
);
|
|
8950
10296
|
try {
|
|
@@ -9086,7 +10432,7 @@ function resolveSessionPreferredModel(db, sessionId, modelMap) {
|
|
|
9086
10432
|
|
|
9087
10433
|
// src/adapters/qoder-cn.ts
|
|
9088
10434
|
function parseQoderCnPaths(filePath) {
|
|
9089
|
-
const parts = filePath.split(
|
|
10435
|
+
const parts = filePath.split(path19.sep);
|
|
9090
10436
|
const subagentsIdx = parts.lastIndexOf("subagents");
|
|
9091
10437
|
let sessionId = "";
|
|
9092
10438
|
let projectName = "";
|
|
@@ -9096,17 +10442,17 @@ function parseQoderCnPaths(filePath) {
|
|
|
9096
10442
|
sessionId = parts[subagentsIdx - 1];
|
|
9097
10443
|
projectName = parts[subagentsIdx - 2];
|
|
9098
10444
|
const projectsIdx = parts.lastIndexOf("projects");
|
|
9099
|
-
configDir2 = parts.slice(0, projectsIdx).join(
|
|
9100
|
-
mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(
|
|
10445
|
+
configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
|
|
10446
|
+
mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path19.sep);
|
|
9101
10447
|
} else {
|
|
9102
10448
|
const filename = parts.at(-1) || "";
|
|
9103
|
-
sessionId =
|
|
10449
|
+
sessionId = path19.basename(filename, ".jsonl");
|
|
9104
10450
|
projectName = parts.at(-2) || "";
|
|
9105
10451
|
if (projectName === "transcript") {
|
|
9106
10452
|
projectName = parts.at(-3) || "";
|
|
9107
10453
|
}
|
|
9108
10454
|
const projectsIdx = parts.lastIndexOf("projects");
|
|
9109
|
-
configDir2 = parts.slice(0, projectsIdx).join(
|
|
10455
|
+
configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
|
|
9110
10456
|
}
|
|
9111
10457
|
return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
|
|
9112
10458
|
}
|
|
@@ -9129,7 +10475,7 @@ function rebuildEventIdentity3(event) {
|
|
|
9129
10475
|
}
|
|
9130
10476
|
async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
|
|
9131
10477
|
try {
|
|
9132
|
-
const content = await
|
|
10478
|
+
const content = await readFile12(dynamicTextsPath, "utf8");
|
|
9133
10479
|
const json = JSON.parse(content);
|
|
9134
10480
|
const texts = json.texts || {};
|
|
9135
10481
|
const map = {};
|
|
@@ -9145,10 +10491,10 @@ async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
|
|
|
9145
10491
|
}
|
|
9146
10492
|
}
|
|
9147
10493
|
async function loadQoderCnModelNames(configDir2, home) {
|
|
9148
|
-
const map = await parseModelNamesFromDynamicTexts(
|
|
10494
|
+
const map = await parseModelNamesFromDynamicTexts(path19.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
9149
10495
|
const siblingConfigDir = configDir2.replace(/\.qoder-cn$/, ".qoder");
|
|
9150
10496
|
if (siblingConfigDir !== configDir2) {
|
|
9151
|
-
const siblingMap = await parseModelNamesFromDynamicTexts(
|
|
10497
|
+
const siblingMap = await parseModelNamesFromDynamicTexts(path19.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
|
|
9152
10498
|
for (const [key, val] of Object.entries(siblingMap)) {
|
|
9153
10499
|
if (!(key in map)) {
|
|
9154
10500
|
map[key] = val;
|
|
@@ -9167,15 +10513,15 @@ async function loadQoderCnModelNames(configDir2, home) {
|
|
|
9167
10513
|
}
|
|
9168
10514
|
async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap) {
|
|
9169
10515
|
const { configDir: configDir2, projectName, sessionId } = parseQoderCnPaths(filePath);
|
|
9170
|
-
const segmentsPath =
|
|
10516
|
+
const segmentsPath = path19.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
|
|
9171
10517
|
const modelCalls = [];
|
|
9172
10518
|
try {
|
|
9173
|
-
const files = await
|
|
10519
|
+
const files = await readdir9(segmentsPath);
|
|
9174
10520
|
for (const file of files) {
|
|
9175
10521
|
if (!file.endsWith(".jsonl")) {
|
|
9176
10522
|
continue;
|
|
9177
10523
|
}
|
|
9178
|
-
const content = await
|
|
10524
|
+
const content = await readFile12(path19.join(segmentsPath, file), "utf8");
|
|
9179
10525
|
let currentTurnIsSubagent = false;
|
|
9180
10526
|
for (const line of content.split("\n").filter(Boolean)) {
|
|
9181
10527
|
const raw = parseJsonLine(line);
|
|
@@ -9209,7 +10555,7 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
|
|
|
9209
10555
|
return modelCalls.filter((call) => call.isSubagent === isSubagentSession);
|
|
9210
10556
|
}
|
|
9211
10557
|
async function parseQoderCnSessionFile(filePath, options) {
|
|
9212
|
-
const text = await
|
|
10558
|
+
const text = await readFile12(filePath, "utf8");
|
|
9213
10559
|
const lines = text.split("\n").filter(Boolean);
|
|
9214
10560
|
const parsedPaths = parseQoderCnPaths(filePath);
|
|
9215
10561
|
const { configDir: configDir2 } = parsedPaths;
|
|
@@ -9220,7 +10566,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
9220
10566
|
let cwd;
|
|
9221
10567
|
let project = projectContext.project;
|
|
9222
10568
|
let model;
|
|
9223
|
-
const home =
|
|
10569
|
+
const home = path19.resolve(stringOption(options.home) || os10.homedir());
|
|
9224
10570
|
const modelMap = await loadQoderCnModelNames(configDir2, home);
|
|
9225
10571
|
const isSubagentSession = filePath.includes("subagents");
|
|
9226
10572
|
const segmentModelCalls = await loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap);
|
|
@@ -9255,7 +10601,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
9255
10601
|
sessionId = stringField(raw, "sessionId") || sessionId;
|
|
9256
10602
|
state.sessionId = sessionId;
|
|
9257
10603
|
cwd = stringField(raw, "cwd") || cwd;
|
|
9258
|
-
project = projectContext.project || (cwd ?
|
|
10604
|
+
project = projectContext.project || (cwd ? path19.basename(cwd) : project || await qoderCnProjectFromFilePath(filePath, options));
|
|
9259
10605
|
if (!ts) {
|
|
9260
10606
|
continue;
|
|
9261
10607
|
}
|
|
@@ -9591,7 +10937,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
9591
10937
|
}
|
|
9592
10938
|
dbModelCalls ??= await loadQoderDbModelCalls("QoderCN", parsedPaths.sessionId, modelMap);
|
|
9593
10939
|
if (dbModelCalls.rootSessionId) {
|
|
9594
|
-
const parentPath =
|
|
10940
|
+
const parentPath = path19.join(path19.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
9595
10941
|
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
9596
10942
|
return withoutSubagentTurnEvents(validEvents).map((event) => rebuildEventIdentity3({
|
|
9597
10943
|
...event,
|
|
@@ -9744,28 +11090,28 @@ function extractQoderAttachedPrompt(content) {
|
|
|
9744
11090
|
}
|
|
9745
11091
|
async function qoderCnProjectContextFromLines(filePath, lines, options, configDir2) {
|
|
9746
11092
|
const { projectName: projectDir, sessionId } = parseQoderCnPaths(filePath);
|
|
9747
|
-
const isSubagent = filePath.includes(`${
|
|
11093
|
+
const isSubagent = filePath.includes(`${path19.sep}subagents${path19.sep}`);
|
|
9748
11094
|
const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
|
|
9749
11095
|
let cwds = [];
|
|
9750
11096
|
for (const line of lines) {
|
|
9751
11097
|
const raw = parseJsonLine(line);
|
|
9752
11098
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
9753
|
-
if (cwd &&
|
|
11099
|
+
if (cwd && path19.isAbsolute(cwd)) {
|
|
9754
11100
|
cwds.push(cwd);
|
|
9755
11101
|
}
|
|
9756
11102
|
}
|
|
9757
11103
|
if (isSubagent) {
|
|
9758
|
-
if (inherited?.cwd &&
|
|
11104
|
+
if (inherited?.cwd && path19.isAbsolute(inherited.cwd)) {
|
|
9759
11105
|
cwds = [inherited.cwd];
|
|
9760
11106
|
} else {
|
|
9761
|
-
const parentSessionPath =
|
|
11107
|
+
const parentSessionPath = path19.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
|
|
9762
11108
|
try {
|
|
9763
|
-
const parentText = await
|
|
11109
|
+
const parentText = await readFile12(parentSessionPath, "utf8");
|
|
9764
11110
|
const parentCwds = [];
|
|
9765
11111
|
for (const line of parentText.split("\n").filter(Boolean)) {
|
|
9766
11112
|
const raw = parseJsonLine(line);
|
|
9767
11113
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
9768
|
-
if (cwd &&
|
|
11114
|
+
if (cwd && path19.isAbsolute(cwd)) {
|
|
9769
11115
|
parentCwds.push(cwd);
|
|
9770
11116
|
}
|
|
9771
11117
|
}
|
|
@@ -9777,7 +11123,7 @@ async function qoderCnProjectContextFromLines(filePath, lines, options, configDi
|
|
|
9777
11123
|
}
|
|
9778
11124
|
}
|
|
9779
11125
|
const root = await gitRootFromCwds2(cwds) || qoderCnProjectRootFromCwds(projectDir, cwds);
|
|
9780
|
-
const project = inherited?.project || (cwds.length > 0 ?
|
|
11126
|
+
const project = inherited?.project || (cwds.length > 0 ? path19.basename(cwds[0]) : root ? path19.basename(root) : await qoderCnProjectFromFilePath(filePath, options));
|
|
9781
11127
|
return {
|
|
9782
11128
|
project,
|
|
9783
11129
|
workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
|
|
@@ -9786,15 +11132,15 @@ async function qoderCnProjectContextFromLines(filePath, lines, options, configDi
|
|
|
9786
11132
|
async function gitRootFromCwds2(cwds) {
|
|
9787
11133
|
const seen = /* @__PURE__ */ new Set();
|
|
9788
11134
|
for (const cwd of cwds) {
|
|
9789
|
-
let current =
|
|
11135
|
+
let current = path19.resolve(cwd);
|
|
9790
11136
|
while (!seen.has(current)) {
|
|
9791
11137
|
seen.add(current);
|
|
9792
11138
|
try {
|
|
9793
|
-
await
|
|
11139
|
+
await stat11(path19.join(current, ".git"));
|
|
9794
11140
|
return current;
|
|
9795
11141
|
} catch {
|
|
9796
11142
|
}
|
|
9797
|
-
const parent =
|
|
11143
|
+
const parent = path19.dirname(current);
|
|
9798
11144
|
if (parent === current) {
|
|
9799
11145
|
break;
|
|
9800
11146
|
}
|
|
@@ -9805,12 +11151,12 @@ async function gitRootFromCwds2(cwds) {
|
|
|
9805
11151
|
}
|
|
9806
11152
|
function qoderCnProjectRootFromCwds(projectDir, cwds) {
|
|
9807
11153
|
for (const cwd of cwds) {
|
|
9808
|
-
let current =
|
|
11154
|
+
let current = path19.resolve(cwd);
|
|
9809
11155
|
while (true) {
|
|
9810
11156
|
if (encodeQoderCnProjectPath(current) === projectDir) {
|
|
9811
11157
|
return current;
|
|
9812
11158
|
}
|
|
9813
|
-
const parent =
|
|
11159
|
+
const parent = path19.dirname(current);
|
|
9814
11160
|
if (parent === current) {
|
|
9815
11161
|
break;
|
|
9816
11162
|
}
|
|
@@ -9820,14 +11166,14 @@ function qoderCnProjectRootFromCwds(projectDir, cwds) {
|
|
|
9820
11166
|
return void 0;
|
|
9821
11167
|
}
|
|
9822
11168
|
function encodeQoderCnProjectPath(value) {
|
|
9823
|
-
return
|
|
11169
|
+
return path19.resolve(value).split(path19.sep).join("-").replaceAll("_", "-");
|
|
9824
11170
|
}
|
|
9825
11171
|
async function qoderCnProjectFromFilePath(filePath, options) {
|
|
9826
|
-
const projectDir =
|
|
9827
|
-
const home = options ?
|
|
11172
|
+
const projectDir = path19.basename(path19.dirname(filePath));
|
|
11173
|
+
const home = options ? path19.resolve(stringOption(options.home) || os10.homedir()) : os10.homedir();
|
|
9828
11174
|
const resolved = await resolveQoderCnProjectPath(projectDir, home);
|
|
9829
11175
|
if (resolved) {
|
|
9830
|
-
return
|
|
11176
|
+
return path19.basename(resolved);
|
|
9831
11177
|
}
|
|
9832
11178
|
const homePrefix = `${encodeQoderCnProjectPath(home)}-`;
|
|
9833
11179
|
if (projectDir.startsWith(homePrefix)) {
|
|
@@ -9847,12 +11193,12 @@ async function resolveQoderCnProjectPath(projectDir, home) {
|
|
|
9847
11193
|
while (projectDir.startsWith(`${currentEncoded}-`)) {
|
|
9848
11194
|
let matchedChild;
|
|
9849
11195
|
try {
|
|
9850
|
-
const entries = await
|
|
11196
|
+
const entries = await readdir9(current, { withFileTypes: true });
|
|
9851
11197
|
for (const entry of entries) {
|
|
9852
11198
|
if (!entry.isDirectory()) {
|
|
9853
11199
|
continue;
|
|
9854
11200
|
}
|
|
9855
|
-
const candidate =
|
|
11201
|
+
const candidate = path19.join(current, entry.name);
|
|
9856
11202
|
const encoded = encodeQoderCnProjectPath(candidate);
|
|
9857
11203
|
if (encoded === projectDir) {
|
|
9858
11204
|
return candidate;
|
|
@@ -9897,9 +11243,9 @@ function hookConfig6() {
|
|
|
9897
11243
|
function qoderCnConfigDir(home, env) {
|
|
9898
11244
|
const override = env?.QODER_CN_CONFIG_DIR;
|
|
9899
11245
|
if (override && override.trim()) {
|
|
9900
|
-
return
|
|
11246
|
+
return path19.resolve(override);
|
|
9901
11247
|
}
|
|
9902
|
-
return
|
|
11248
|
+
return path19.join(home, ".qoder-cn");
|
|
9903
11249
|
}
|
|
9904
11250
|
function createQoderCnAdapter() {
|
|
9905
11251
|
return {
|
|
@@ -9911,27 +11257,27 @@ function createQoderCnAdapter() {
|
|
|
9911
11257
|
return qoderCnConfigDir(home, env);
|
|
9912
11258
|
},
|
|
9913
11259
|
installedPath(home, env) {
|
|
9914
|
-
return
|
|
11260
|
+
return path19.join(qoderCnConfigDir(home, env), "settings.json");
|
|
9915
11261
|
},
|
|
9916
11262
|
async isInstalled(home, env) {
|
|
9917
11263
|
return isHooksJsonInstalled(
|
|
9918
|
-
|
|
11264
|
+
path19.join(qoderCnConfigDir(home, env), "settings.json"),
|
|
9919
11265
|
"vibetime hook --agent qoder-cn"
|
|
9920
11266
|
);
|
|
9921
11267
|
},
|
|
9922
11268
|
installEntries(home, env) {
|
|
9923
11269
|
return [{
|
|
9924
11270
|
kind: "hooks-json",
|
|
9925
|
-
path:
|
|
11271
|
+
path: path19.join(qoderCnConfigDir(home, env), "settings.json"),
|
|
9926
11272
|
content: hookConfig6()
|
|
9927
11273
|
}];
|
|
9928
11274
|
},
|
|
9929
11275
|
sourcePaths(home, env) {
|
|
9930
11276
|
const base = qoderCnConfigDir(home, env);
|
|
9931
11277
|
return [
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
11278
|
+
path19.join(base, "projects"),
|
|
11279
|
+
path19.join(base, ".qoder.json"),
|
|
11280
|
+
path19.join(home, ".qoder.json")
|
|
9935
11281
|
];
|
|
9936
11282
|
},
|
|
9937
11283
|
parseSessionFile: parseQoderCnSessionFile
|
|
@@ -9940,11 +11286,11 @@ function createQoderCnAdapter() {
|
|
|
9940
11286
|
|
|
9941
11287
|
// src/adapters/qoder.ts
|
|
9942
11288
|
import { existsSync } from "node:fs";
|
|
9943
|
-
import { readdir as
|
|
9944
|
-
import
|
|
9945
|
-
import
|
|
11289
|
+
import { readdir as readdir10, readFile as readFile13, stat as stat12 } from "node:fs/promises";
|
|
11290
|
+
import os11 from "node:os";
|
|
11291
|
+
import path20 from "node:path";
|
|
9946
11292
|
function parseQoderPaths(filePath) {
|
|
9947
|
-
const parts = filePath.split(
|
|
11293
|
+
const parts = filePath.split(path20.sep);
|
|
9948
11294
|
const subagentsIdx = parts.lastIndexOf("subagents");
|
|
9949
11295
|
let sessionId = "";
|
|
9950
11296
|
let projectName = "";
|
|
@@ -9954,17 +11300,17 @@ function parseQoderPaths(filePath) {
|
|
|
9954
11300
|
sessionId = parts[subagentsIdx - 1];
|
|
9955
11301
|
projectName = parts[subagentsIdx - 2];
|
|
9956
11302
|
const projectsIdx = parts.lastIndexOf("projects");
|
|
9957
|
-
configDir2 = parts.slice(0, projectsIdx).join(
|
|
9958
|
-
mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(
|
|
11303
|
+
configDir2 = parts.slice(0, projectsIdx).join(path20.sep);
|
|
11304
|
+
mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path20.sep);
|
|
9959
11305
|
} else {
|
|
9960
11306
|
const filename = parts.at(-1) || "";
|
|
9961
|
-
sessionId =
|
|
11307
|
+
sessionId = path20.basename(filename, ".jsonl");
|
|
9962
11308
|
projectName = parts.at(-2) || "";
|
|
9963
11309
|
if (projectName === "transcript") {
|
|
9964
11310
|
projectName = parts.at(-3) || "";
|
|
9965
11311
|
}
|
|
9966
11312
|
const projectsIdx = parts.lastIndexOf("projects");
|
|
9967
|
-
configDir2 = parts.slice(0, projectsIdx).join(
|
|
11313
|
+
configDir2 = parts.slice(0, projectsIdx).join(path20.sep);
|
|
9968
11314
|
}
|
|
9969
11315
|
return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
|
|
9970
11316
|
}
|
|
@@ -9987,7 +11333,7 @@ function rebuildEventIdentity4(event) {
|
|
|
9987
11333
|
}
|
|
9988
11334
|
async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
|
|
9989
11335
|
try {
|
|
9990
|
-
const content = await
|
|
11336
|
+
const content = await readFile13(dynamicTextsPath, "utf8");
|
|
9991
11337
|
const json = JSON.parse(content);
|
|
9992
11338
|
const texts = json.texts || {};
|
|
9993
11339
|
const map = {};
|
|
@@ -10003,10 +11349,10 @@ async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
|
|
|
10003
11349
|
}
|
|
10004
11350
|
}
|
|
10005
11351
|
async function loadQoderModelNames(configDir2, home) {
|
|
10006
|
-
const map = await parseModelNamesFromDynamicTexts2(
|
|
11352
|
+
const map = await parseModelNamesFromDynamicTexts2(path20.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
10007
11353
|
const siblingConfigDir = configDir2.replace(/\.qoder$/, ".qoder-cn");
|
|
10008
11354
|
if (siblingConfigDir !== configDir2) {
|
|
10009
|
-
const siblingMap = await parseModelNamesFromDynamicTexts2(
|
|
11355
|
+
const siblingMap = await parseModelNamesFromDynamicTexts2(path20.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
|
|
10010
11356
|
for (const [key, val] of Object.entries(siblingMap)) {
|
|
10011
11357
|
if (!(key in map)) {
|
|
10012
11358
|
map[key] = val;
|
|
@@ -10014,11 +11360,11 @@ async function loadQoderModelNames(configDir2, home) {
|
|
|
10014
11360
|
}
|
|
10015
11361
|
}
|
|
10016
11362
|
if (isQwenworkConfigRoot(configDir2)) {
|
|
10017
|
-
for (const dir of [
|
|
10018
|
-
if (
|
|
11363
|
+
for (const dir of [path20.join(home, ".qoder"), path20.join(home, ".qoder-cn")]) {
|
|
11364
|
+
if (path20.resolve(dir) === path20.resolve(configDir2)) {
|
|
10019
11365
|
continue;
|
|
10020
11366
|
}
|
|
10021
|
-
const fallbackMap = await parseModelNamesFromDynamicTexts2(
|
|
11367
|
+
const fallbackMap = await parseModelNamesFromDynamicTexts2(path20.join(dir, ".auth", "dynamic-texts.json"));
|
|
10022
11368
|
for (const [key, val] of Object.entries(fallbackMap)) {
|
|
10023
11369
|
if (!(key in map)) {
|
|
10024
11370
|
map[key] = val;
|
|
@@ -10037,25 +11383,25 @@ async function loadQoderModelNames(configDir2, home) {
|
|
|
10037
11383
|
return map;
|
|
10038
11384
|
}
|
|
10039
11385
|
function isQwenworkConfigRoot(configDir2) {
|
|
10040
|
-
const name =
|
|
11386
|
+
const name = path20.basename(path20.resolve(configDir2));
|
|
10041
11387
|
if (name === ".qwenworkcn" || name === ".qwenwork") {
|
|
10042
11388
|
return true;
|
|
10043
11389
|
}
|
|
10044
11390
|
const override = process.env.QWENWORK_CONFIG_DIR;
|
|
10045
|
-
return Boolean(override && override.trim() &&
|
|
11391
|
+
return Boolean(override && override.trim() && path20.basename(path20.resolve(override)) === name);
|
|
10046
11392
|
}
|
|
10047
11393
|
var FAILED_SEGMENT_STOP_REASONS = /* @__PURE__ */ new Set(["cancelled", "canceled", "error", "failed", "refusal"]);
|
|
10048
11394
|
async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap) {
|
|
10049
11395
|
const { configDir: configDir2, projectName, sessionId } = parseQoderPaths(filePath);
|
|
10050
|
-
const segmentsPath =
|
|
11396
|
+
const segmentsPath = path20.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
|
|
10051
11397
|
const modelCalls = [];
|
|
10052
11398
|
try {
|
|
10053
|
-
const files = await
|
|
11399
|
+
const files = await readdir10(segmentsPath);
|
|
10054
11400
|
for (const file of files) {
|
|
10055
11401
|
if (!file.endsWith(".jsonl")) {
|
|
10056
11402
|
continue;
|
|
10057
11403
|
}
|
|
10058
|
-
const content = await
|
|
11404
|
+
const content = await readFile13(path20.join(segmentsPath, file), "utf8");
|
|
10059
11405
|
let currentTurnIsSubagent = false;
|
|
10060
11406
|
for (const line of content.split("\n").filter(Boolean)) {
|
|
10061
11407
|
const raw = parseJsonLine(line);
|
|
@@ -10089,7 +11435,7 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
|
|
|
10089
11435
|
return modelCalls.filter((call) => call.isSubagent === isSubagentSession);
|
|
10090
11436
|
}
|
|
10091
11437
|
async function parseQoderSessionFile(filePath, options) {
|
|
10092
|
-
const text = await
|
|
11438
|
+
const text = await readFile13(filePath, "utf8");
|
|
10093
11439
|
const lines = text.split("\n").filter(Boolean);
|
|
10094
11440
|
const parsedPaths = parseQoderPaths(filePath);
|
|
10095
11441
|
const { configDir: configDir2 } = parsedPaths;
|
|
@@ -10100,7 +11446,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
10100
11446
|
let cwd;
|
|
10101
11447
|
let project = projectContext.project;
|
|
10102
11448
|
let model;
|
|
10103
|
-
const home =
|
|
11449
|
+
const home = path20.resolve(stringOption(options.home) || os11.homedir());
|
|
10104
11450
|
const modelMap = await loadQoderModelNames(configDir2, home);
|
|
10105
11451
|
const qwenworkRoot = isQwenworkConfigRoot(configDir2);
|
|
10106
11452
|
const isSubagentSession = filePath.includes("subagents");
|
|
@@ -10136,7 +11482,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
10136
11482
|
sessionId = stringField(raw, "sessionId") || sessionId;
|
|
10137
11483
|
state.sessionId = sessionId;
|
|
10138
11484
|
cwd = stringField(raw, "cwd") || cwd;
|
|
10139
|
-
project = projectContext.project || (cwd ?
|
|
11485
|
+
project = projectContext.project || (cwd ? path20.basename(cwd) : project || await qoderProjectFromFilePath(filePath, options));
|
|
10140
11486
|
if (!ts) {
|
|
10141
11487
|
continue;
|
|
10142
11488
|
}
|
|
@@ -10447,7 +11793,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
10447
11793
|
}
|
|
10448
11794
|
dbModelCalls ??= await loadQoderDbModelCalls("Qoder", parsedPaths.sessionId, modelMap);
|
|
10449
11795
|
if (dbModelCalls.rootSessionId) {
|
|
10450
|
-
const parentPath =
|
|
11796
|
+
const parentPath = path20.join(path20.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
10451
11797
|
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
10452
11798
|
return withoutSubagentTurnEvents(validEvents).map((event) => rebuildEventIdentity4({
|
|
10453
11799
|
...event,
|
|
@@ -10564,41 +11910,41 @@ function qoderExtractText(value) {
|
|
|
10564
11910
|
}
|
|
10565
11911
|
async function qoderProjectContextFromLines(filePath, lines, options, configDir2) {
|
|
10566
11912
|
const { projectName: projectDir, sessionId } = parseQoderPaths(filePath);
|
|
10567
|
-
const isSubagent = filePath.includes(`${
|
|
11913
|
+
const isSubagent = filePath.includes(`${path20.sep}subagents${path20.sep}`);
|
|
10568
11914
|
const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
|
|
10569
11915
|
let cwds = [];
|
|
10570
11916
|
const workspaceDirs = [];
|
|
10571
11917
|
for (const line of lines) {
|
|
10572
11918
|
const raw = parseJsonLine(line);
|
|
10573
11919
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
10574
|
-
if (cwd &&
|
|
11920
|
+
if (cwd && path20.isAbsolute(cwd)) {
|
|
10575
11921
|
cwds.push(cwd);
|
|
10576
11922
|
}
|
|
10577
11923
|
if (raw && stringField(raw, "type") === "workspace-directories") {
|
|
10578
11924
|
for (const dir of arrayField5(raw, "directories")) {
|
|
10579
|
-
if (typeof dir === "string" &&
|
|
11925
|
+
if (typeof dir === "string" && path20.isAbsolute(dir)) {
|
|
10580
11926
|
workspaceDirs.push(dir);
|
|
10581
11927
|
}
|
|
10582
11928
|
}
|
|
10583
11929
|
}
|
|
10584
11930
|
}
|
|
10585
11931
|
if (isSubagent) {
|
|
10586
|
-
if (inherited?.cwd &&
|
|
11932
|
+
if (inherited?.cwd && path20.isAbsolute(inherited.cwd)) {
|
|
10587
11933
|
cwds = [inherited.cwd];
|
|
10588
11934
|
} else {
|
|
10589
|
-
const parentSessionPath =
|
|
11935
|
+
const parentSessionPath = path20.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
|
|
10590
11936
|
try {
|
|
10591
|
-
const parentText = await
|
|
11937
|
+
const parentText = await readFile13(parentSessionPath, "utf8");
|
|
10592
11938
|
const parentCwds = [];
|
|
10593
11939
|
for (const line of parentText.split("\n").filter(Boolean)) {
|
|
10594
11940
|
const raw = parseJsonLine(line);
|
|
10595
11941
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
10596
|
-
if (cwd &&
|
|
11942
|
+
if (cwd && path20.isAbsolute(cwd)) {
|
|
10597
11943
|
parentCwds.push(cwd);
|
|
10598
11944
|
}
|
|
10599
11945
|
if (workspaceDirs.length === 0 && raw && stringField(raw, "type") === "workspace-directories") {
|
|
10600
11946
|
for (const dir of arrayField5(raw, "directories")) {
|
|
10601
|
-
if (typeof dir === "string" &&
|
|
11947
|
+
if (typeof dir === "string" && path20.isAbsolute(dir)) {
|
|
10602
11948
|
workspaceDirs.push(dir);
|
|
10603
11949
|
}
|
|
10604
11950
|
}
|
|
@@ -10618,29 +11964,29 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
10618
11964
|
}
|
|
10619
11965
|
}
|
|
10620
11966
|
const root = await gitRootFromCwds3(cwds) || qoderProjectRootFromCwds(projectDir, cwds);
|
|
10621
|
-
const project = inherited?.project || (cwds.length > 0 ?
|
|
11967
|
+
const project = inherited?.project || (cwds.length > 0 ? path20.basename(cwds[0]) : root ? path20.basename(root) : await qoderProjectFromFilePath(filePath, options));
|
|
10622
11968
|
return {
|
|
10623
11969
|
project,
|
|
10624
11970
|
workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
|
|
10625
11971
|
};
|
|
10626
11972
|
}
|
|
10627
11973
|
function pathInsideDir(candidate, dir) {
|
|
10628
|
-
const resolvedDir =
|
|
10629
|
-
const resolved =
|
|
10630
|
-
return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${
|
|
11974
|
+
const resolvedDir = path20.resolve(dir);
|
|
11975
|
+
const resolved = path20.resolve(candidate);
|
|
11976
|
+
return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${path20.sep}`);
|
|
10631
11977
|
}
|
|
10632
11978
|
async function gitRootFromCwds3(cwds) {
|
|
10633
11979
|
const seen = /* @__PURE__ */ new Set();
|
|
10634
11980
|
for (const cwd of cwds) {
|
|
10635
|
-
let current =
|
|
11981
|
+
let current = path20.resolve(cwd);
|
|
10636
11982
|
while (!seen.has(current)) {
|
|
10637
11983
|
seen.add(current);
|
|
10638
11984
|
try {
|
|
10639
|
-
await
|
|
11985
|
+
await stat12(path20.join(current, ".git"));
|
|
10640
11986
|
return current;
|
|
10641
11987
|
} catch {
|
|
10642
11988
|
}
|
|
10643
|
-
const parent =
|
|
11989
|
+
const parent = path20.dirname(current);
|
|
10644
11990
|
if (parent === current) {
|
|
10645
11991
|
break;
|
|
10646
11992
|
}
|
|
@@ -10651,12 +11997,12 @@ async function gitRootFromCwds3(cwds) {
|
|
|
10651
11997
|
}
|
|
10652
11998
|
function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
10653
11999
|
for (const cwd of cwds) {
|
|
10654
|
-
let current =
|
|
12000
|
+
let current = path20.resolve(cwd);
|
|
10655
12001
|
while (true) {
|
|
10656
12002
|
if (qoderEncodedVariants(current).includes(projectDir)) {
|
|
10657
12003
|
return current;
|
|
10658
12004
|
}
|
|
10659
|
-
const parent =
|
|
12005
|
+
const parent = path20.dirname(current);
|
|
10660
12006
|
if (parent === current) {
|
|
10661
12007
|
break;
|
|
10662
12008
|
}
|
|
@@ -10666,7 +12012,7 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
|
10666
12012
|
return void 0;
|
|
10667
12013
|
}
|
|
10668
12014
|
function rawQoderProjectPath(value) {
|
|
10669
|
-
return
|
|
12015
|
+
return path20.resolve(value).split(path20.sep).join("-");
|
|
10670
12016
|
}
|
|
10671
12017
|
function qoderEncodedVariants(value) {
|
|
10672
12018
|
const raw = rawQoderProjectPath(value);
|
|
@@ -10687,11 +12033,11 @@ function qoderEncodedProjectSuffix(projectDir, home) {
|
|
|
10687
12033
|
return void 0;
|
|
10688
12034
|
}
|
|
10689
12035
|
async function qoderProjectFromFilePath(filePath, options) {
|
|
10690
|
-
const projectDir =
|
|
10691
|
-
const home = options ?
|
|
12036
|
+
const projectDir = path20.basename(path20.dirname(filePath));
|
|
12037
|
+
const home = options ? path20.resolve(stringOption(options.home) || os11.homedir()) : os11.homedir();
|
|
10692
12038
|
const resolved = await resolveQoderProjectPath(projectDir, home);
|
|
10693
12039
|
if (resolved) {
|
|
10694
|
-
return
|
|
12040
|
+
return path20.basename(resolved);
|
|
10695
12041
|
}
|
|
10696
12042
|
const suffix = qoderEncodedProjectSuffix(projectDir, home);
|
|
10697
12043
|
if (suffix) {
|
|
@@ -10712,12 +12058,12 @@ async function resolveQoderProjectPath(projectDir, home) {
|
|
|
10712
12058
|
while (currentEncodedVariants.some((prefix) => projectDir.startsWith(`${prefix}-`))) {
|
|
10713
12059
|
let matchedChild;
|
|
10714
12060
|
try {
|
|
10715
|
-
const entries = await
|
|
12061
|
+
const entries = await readdir10(current, { withFileTypes: true });
|
|
10716
12062
|
for (const entry of entries) {
|
|
10717
12063
|
if (!entry.isDirectory()) {
|
|
10718
12064
|
continue;
|
|
10719
12065
|
}
|
|
10720
|
-
const candidate =
|
|
12066
|
+
const candidate = path20.join(current, entry.name);
|
|
10721
12067
|
const candidateVariants = qoderEncodedVariants(candidate);
|
|
10722
12068
|
if (candidateVariants.includes(projectDir)) {
|
|
10723
12069
|
return candidate;
|
|
@@ -10762,19 +12108,19 @@ function hookConfig7() {
|
|
|
10762
12108
|
function qoderConfigDir(home, env) {
|
|
10763
12109
|
const override = env?.QODER_CONFIG_DIR;
|
|
10764
12110
|
if (override && override.trim()) {
|
|
10765
|
-
return
|
|
12111
|
+
return path20.resolve(override);
|
|
10766
12112
|
}
|
|
10767
|
-
return
|
|
12113
|
+
return path20.join(home, ".qoder");
|
|
10768
12114
|
}
|
|
10769
12115
|
function qwenworkConfigDir(home, env) {
|
|
10770
12116
|
const override = env?.QWENWORK_CONFIG_DIR;
|
|
10771
12117
|
if (override && override.trim()) {
|
|
10772
|
-
return
|
|
12118
|
+
return path20.resolve(override);
|
|
10773
12119
|
}
|
|
10774
|
-
return
|
|
12120
|
+
return path20.join(home, ".qwenworkcn");
|
|
10775
12121
|
}
|
|
10776
12122
|
function qoderConfigDirs(home, env) {
|
|
10777
|
-
return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) =>
|
|
12123
|
+
return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) => path20.resolve(dir)))];
|
|
10778
12124
|
}
|
|
10779
12125
|
function createQoderAdapter() {
|
|
10780
12126
|
return {
|
|
@@ -10786,11 +12132,11 @@ function createQoderAdapter() {
|
|
|
10786
12132
|
return qoderConfigDir(home, env);
|
|
10787
12133
|
},
|
|
10788
12134
|
installedPath(home, env) {
|
|
10789
|
-
return
|
|
12135
|
+
return path20.join(qoderConfigDir(home, env), "settings.json");
|
|
10790
12136
|
},
|
|
10791
12137
|
async isInstalled(home, env) {
|
|
10792
12138
|
return isHooksJsonInstalled(
|
|
10793
|
-
|
|
12139
|
+
path20.join(qoderConfigDir(home, env), "settings.json"),
|
|
10794
12140
|
"vibetime hook --agent qoder"
|
|
10795
12141
|
);
|
|
10796
12142
|
},
|
|
@@ -10799,16 +12145,16 @@ function createQoderAdapter() {
|
|
|
10799
12145
|
const targets = [primary, ...variants.filter((dir) => existsSync(dir))];
|
|
10800
12146
|
return targets.map((base) => ({
|
|
10801
12147
|
kind: "hooks-json",
|
|
10802
|
-
path:
|
|
12148
|
+
path: path20.join(base, "settings.json"),
|
|
10803
12149
|
content: hookConfig7()
|
|
10804
12150
|
}));
|
|
10805
12151
|
},
|
|
10806
12152
|
sourcePaths(home, env) {
|
|
10807
|
-
const paths = qoderConfigDirs(home, env).map((base2) =>
|
|
12153
|
+
const paths = qoderConfigDirs(home, env).map((base2) => path20.join(base2, "projects"));
|
|
10808
12154
|
const base = qoderConfigDir(home, env);
|
|
10809
12155
|
paths.push(
|
|
10810
|
-
|
|
10811
|
-
|
|
12156
|
+
path20.join(base, ".qoder.json"),
|
|
12157
|
+
path20.join(home, ".qoder.json")
|
|
10812
12158
|
);
|
|
10813
12159
|
return paths;
|
|
10814
12160
|
},
|
|
@@ -10844,27 +12190,27 @@ function normalizeId(id) {
|
|
|
10844
12190
|
}
|
|
10845
12191
|
|
|
10846
12192
|
// src/adapters/workbuddy.ts
|
|
10847
|
-
import { readdir as
|
|
10848
|
-
import
|
|
12193
|
+
import { readdir as readdir11, readFile as readFile14, stat as stat13 } from "node:fs/promises";
|
|
12194
|
+
import path21 from "node:path";
|
|
10849
12195
|
function workbuddyProjectsDir(home, env) {
|
|
10850
12196
|
const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
|
|
10851
12197
|
if (override && override.trim()) {
|
|
10852
|
-
return
|
|
12198
|
+
return path21.resolve(override, override.endsWith("projects") ? "" : "projects");
|
|
10853
12199
|
}
|
|
10854
|
-
return
|
|
12200
|
+
return path21.join(home, ".workbuddy", "projects");
|
|
10855
12201
|
}
|
|
10856
12202
|
function workbuddyBaseDir(home, env) {
|
|
10857
12203
|
const override = env?.WORKBUDDY_HOME;
|
|
10858
12204
|
if (override && override.trim()) {
|
|
10859
|
-
return
|
|
12205
|
+
return path21.resolve(override);
|
|
10860
12206
|
}
|
|
10861
|
-
return
|
|
12207
|
+
return path21.join(home, ".workbuddy");
|
|
10862
12208
|
}
|
|
10863
12209
|
function projectFromCwd(cwd, fallback) {
|
|
10864
12210
|
if (!cwd) {
|
|
10865
12211
|
return fallback;
|
|
10866
12212
|
}
|
|
10867
|
-
return
|
|
12213
|
+
return path21.basename(cwd) || fallback;
|
|
10868
12214
|
}
|
|
10869
12215
|
function sourceHash(filePath) {
|
|
10870
12216
|
return `sha256:${createStableHash(filePath)}`;
|
|
@@ -10982,7 +12328,7 @@ function toolCallFailed(record) {
|
|
|
10982
12328
|
return status === "failed" || status === "incomplete" || record.is_error === true || providerData.error != null || providerData.isError === true;
|
|
10983
12329
|
}
|
|
10984
12330
|
async function readWorkbuddyLines(filePath) {
|
|
10985
|
-
const text = await
|
|
12331
|
+
const text = await readFile14(filePath, "utf8");
|
|
10986
12332
|
return text.split(/\r?\n/).map((line, index) => {
|
|
10987
12333
|
if (!line.trim()) {
|
|
10988
12334
|
return void 0;
|
|
@@ -11002,8 +12348,8 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
11002
12348
|
}
|
|
11003
12349
|
const events = [];
|
|
11004
12350
|
const first = lines[0].record;
|
|
11005
|
-
const sessionId = stringField(first, "sessionId") ||
|
|
11006
|
-
const fallbackProject =
|
|
12351
|
+
const sessionId = stringField(first, "sessionId") || path21.basename(filePath, ".jsonl");
|
|
12352
|
+
const fallbackProject = path21.basename(path21.dirname(filePath));
|
|
11007
12353
|
const cwd = lines.map((line) => stringField(line.record, "cwd")).find(Boolean);
|
|
11008
12354
|
const project = projectFromCwd(cwd, fallbackProject);
|
|
11009
12355
|
const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
|
|
@@ -11287,17 +12633,17 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
|
|
|
11287
12633
|
const base = sourceRoot || workbuddyProjectsDir(home, env);
|
|
11288
12634
|
const files = [];
|
|
11289
12635
|
try {
|
|
11290
|
-
const projects = await
|
|
12636
|
+
const projects = await readdir11(base, { withFileTypes: true });
|
|
11291
12637
|
for (const project of projects) {
|
|
11292
12638
|
if (!project.isDirectory()) {
|
|
11293
12639
|
continue;
|
|
11294
12640
|
}
|
|
11295
|
-
const projectDir =
|
|
11296
|
-
const entries = await
|
|
12641
|
+
const projectDir = path21.join(base, project.name);
|
|
12642
|
+
const entries = await readdir11(projectDir, { withFileTypes: true });
|
|
11297
12643
|
for (const entry of entries) {
|
|
11298
12644
|
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
11299
|
-
const filePath =
|
|
11300
|
-
const info = await
|
|
12645
|
+
const filePath = path21.join(projectDir, entry.name);
|
|
12646
|
+
const info = await stat13(filePath);
|
|
11301
12647
|
files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
|
|
11302
12648
|
}
|
|
11303
12649
|
}
|
|
@@ -11334,18 +12680,18 @@ function createWorkbuddyAdapter() {
|
|
|
11334
12680
|
return workbuddyProjectsDir(home, env);
|
|
11335
12681
|
},
|
|
11336
12682
|
installedPath(home, env) {
|
|
11337
|
-
return
|
|
12683
|
+
return path21.join(workbuddyBaseDir(home, env), "settings.json");
|
|
11338
12684
|
},
|
|
11339
12685
|
async isInstalled(home, env) {
|
|
11340
12686
|
return isHooksJsonInstalled(
|
|
11341
|
-
|
|
12687
|
+
path21.join(workbuddyBaseDir(home, env), "settings.json"),
|
|
11342
12688
|
"vibetime hook --agent workbuddy"
|
|
11343
12689
|
);
|
|
11344
12690
|
},
|
|
11345
12691
|
installEntries(home, env) {
|
|
11346
12692
|
return [{
|
|
11347
12693
|
kind: "hooks-json",
|
|
11348
|
-
path:
|
|
12694
|
+
path: path21.join(workbuddyBaseDir(home, env), "settings.json"),
|
|
11349
12695
|
content: hookConfig8()
|
|
11350
12696
|
}];
|
|
11351
12697
|
},
|
|
@@ -11358,26 +12704,26 @@ function createWorkbuddyAdapter() {
|
|
|
11358
12704
|
|
|
11359
12705
|
// src/adapters/zcode.ts
|
|
11360
12706
|
import { execFile } from "node:child_process";
|
|
11361
|
-
import { readFile as
|
|
11362
|
-
import
|
|
12707
|
+
import { readFile as readFile15, stat as stat14 } from "node:fs/promises";
|
|
12708
|
+
import path22 from "node:path";
|
|
11363
12709
|
import { promisify as promisify2 } from "node:util";
|
|
11364
12710
|
init_fs();
|
|
11365
12711
|
var execFileAsync = promisify2(execFile);
|
|
11366
12712
|
function zcodeCliDir(home, env) {
|
|
11367
12713
|
const override = env?.ZCODE_CLI_DIR || env?.ZCODE_HOME;
|
|
11368
12714
|
if (override && override.trim()) {
|
|
11369
|
-
return
|
|
12715
|
+
return path22.resolve(override, override.endsWith("cli") ? "" : "cli");
|
|
11370
12716
|
}
|
|
11371
|
-
return
|
|
12717
|
+
return path22.join(home, ".zcode", "cli");
|
|
11372
12718
|
}
|
|
11373
12719
|
function zcodeDbPath(home, env) {
|
|
11374
|
-
return
|
|
12720
|
+
return path22.join(zcodeCliDir(home, env), "db", "db.sqlite");
|
|
11375
12721
|
}
|
|
11376
12722
|
var providerNameCache = null;
|
|
11377
12723
|
async function loadProviderNames(configPath2) {
|
|
11378
12724
|
let fileMtime = 0;
|
|
11379
12725
|
try {
|
|
11380
|
-
const info = await
|
|
12726
|
+
const info = await stat14(configPath2);
|
|
11381
12727
|
fileMtime = info.mtimeMs;
|
|
11382
12728
|
} catch {
|
|
11383
12729
|
return /* @__PURE__ */ new Map();
|
|
@@ -11387,7 +12733,7 @@ async function loadProviderNames(configPath2) {
|
|
|
11387
12733
|
}
|
|
11388
12734
|
const map = /* @__PURE__ */ new Map();
|
|
11389
12735
|
try {
|
|
11390
|
-
const raw = await
|
|
12736
|
+
const raw = await readFile15(configPath2, "utf-8");
|
|
11391
12737
|
const config = JSON.parse(raw);
|
|
11392
12738
|
const providers = config?.provider;
|
|
11393
12739
|
if (isPlainObject(providers)) {
|
|
@@ -11405,7 +12751,7 @@ function sourceHash2(filePath) {
|
|
|
11405
12751
|
return `sha256:${createStableHash(filePath)}`;
|
|
11406
12752
|
}
|
|
11407
12753
|
function projectFromDirectory(directory) {
|
|
11408
|
-
return directory ?
|
|
12754
|
+
return directory ? path22.basename(directory) || "zcode" : "zcode";
|
|
11409
12755
|
}
|
|
11410
12756
|
function isoFromMs(value) {
|
|
11411
12757
|
return timestampFrom(typeof value === "number" ? value : Number(value));
|
|
@@ -11589,16 +12935,16 @@ async function parseZCodeDb(filePath, options) {
|
|
|
11589
12935
|
if (rows.length === 0) {
|
|
11590
12936
|
return [];
|
|
11591
12937
|
}
|
|
11592
|
-
let candidate =
|
|
12938
|
+
let candidate = path22.resolve(filePath);
|
|
11593
12939
|
let configPath2 = "";
|
|
11594
12940
|
for (let i = 0; i < 12; i++) {
|
|
11595
|
-
const probe =
|
|
12941
|
+
const probe = path22.join(candidate, ".zcode", "v2", "config.json");
|
|
11596
12942
|
try {
|
|
11597
|
-
await
|
|
12943
|
+
await stat14(probe);
|
|
11598
12944
|
configPath2 = probe;
|
|
11599
12945
|
break;
|
|
11600
12946
|
} catch {
|
|
11601
|
-
const parent =
|
|
12947
|
+
const parent = path22.dirname(candidate);
|
|
11602
12948
|
if (parent === candidate) break;
|
|
11603
12949
|
candidate = parent;
|
|
11604
12950
|
}
|
|
@@ -11818,9 +13164,9 @@ async function parseZCodeDb(filePath, options) {
|
|
|
11818
13164
|
}
|
|
11819
13165
|
async function zcodeBackfillFiles(sourceRoot, home, env) {
|
|
11820
13166
|
const candidate = sourceRoot || zcodeDbPath(home, env);
|
|
11821
|
-
const filePath = candidate.endsWith(".sqlite") ? candidate :
|
|
13167
|
+
const filePath = candidate.endsWith(".sqlite") ? candidate : path22.join(candidate, "db", "db.sqlite");
|
|
11822
13168
|
try {
|
|
11823
|
-
const info = await
|
|
13169
|
+
const info = await stat14(filePath);
|
|
11824
13170
|
return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
|
|
11825
13171
|
} catch {
|
|
11826
13172
|
return [];
|
|
@@ -11852,50 +13198,50 @@ function createZCodeAdapter() {
|
|
|
11852
13198
|
}
|
|
11853
13199
|
|
|
11854
13200
|
// src/adapters/zed.ts
|
|
11855
|
-
import
|
|
11856
|
-
import
|
|
13201
|
+
import os12 from "node:os";
|
|
13202
|
+
import path23 from "node:path";
|
|
11857
13203
|
function zedThreadsCandidates(home, env) {
|
|
11858
13204
|
const candidates = [];
|
|
11859
13205
|
const platform2 = process.platform;
|
|
11860
13206
|
if (platform2 === "darwin") {
|
|
11861
|
-
candidates.push(
|
|
13207
|
+
candidates.push(path23.join(home, "Library", "Application Support", "Zed", "threads", "threads.db"));
|
|
11862
13208
|
} else if (platform2 === "win32") {
|
|
11863
13209
|
const appdata = env?.APPDATA;
|
|
11864
13210
|
if (appdata && appdata.trim()) {
|
|
11865
|
-
candidates.push(
|
|
13211
|
+
candidates.push(path23.join(path23.resolve(appdata), "Zed", "threads", "threads.db"));
|
|
11866
13212
|
}
|
|
11867
|
-
candidates.push(
|
|
13213
|
+
candidates.push(path23.join(home, "AppData", "Roaming", "Zed", "threads", "threads.db"));
|
|
11868
13214
|
} else {
|
|
11869
13215
|
const xdgData = env?.XDG_DATA_HOME;
|
|
11870
13216
|
if (xdgData && xdgData.trim()) {
|
|
11871
|
-
candidates.push(
|
|
13217
|
+
candidates.push(path23.join(path23.resolve(xdgData), "zed", "threads", "threads.db"));
|
|
11872
13218
|
}
|
|
11873
|
-
candidates.push(
|
|
13219
|
+
candidates.push(path23.join(home, ".local", "share", "zed", "threads", "threads.db"));
|
|
11874
13220
|
const xdgConfig = env?.XDG_CONFIG_HOME;
|
|
11875
13221
|
if (xdgConfig && xdgConfig.trim()) {
|
|
11876
|
-
candidates.push(
|
|
13222
|
+
candidates.push(path23.join(path23.resolve(xdgConfig), "zed", "threads", "threads.db"));
|
|
11877
13223
|
}
|
|
11878
|
-
candidates.push(
|
|
13224
|
+
candidates.push(path23.join(home, ".config", "zed", "threads", "threads.db"));
|
|
11879
13225
|
}
|
|
11880
13226
|
return candidates;
|
|
11881
13227
|
}
|
|
11882
13228
|
function zedConfigDir(home, env) {
|
|
11883
13229
|
const platform2 = process.platform;
|
|
11884
13230
|
if (platform2 === "darwin") {
|
|
11885
|
-
return
|
|
13231
|
+
return path23.join(home, "Library", "Application Support", "Zed");
|
|
11886
13232
|
}
|
|
11887
13233
|
if (platform2 === "win32") {
|
|
11888
13234
|
const appdata = env?.APPDATA;
|
|
11889
13235
|
if (appdata && appdata.trim()) {
|
|
11890
|
-
return
|
|
13236
|
+
return path23.join(path23.resolve(appdata), "Zed");
|
|
11891
13237
|
}
|
|
11892
|
-
return
|
|
13238
|
+
return path23.join(home, "AppData", "Roaming", "Zed");
|
|
11893
13239
|
}
|
|
11894
13240
|
const xdgConfig = env?.XDG_CONFIG_HOME;
|
|
11895
13241
|
if (xdgConfig && xdgConfig.trim()) {
|
|
11896
|
-
return
|
|
13242
|
+
return path23.join(path23.resolve(xdgConfig), "zed");
|
|
11897
13243
|
}
|
|
11898
|
-
return
|
|
13244
|
+
return path23.join(home, ".config", "zed");
|
|
11899
13245
|
}
|
|
11900
13246
|
function baseZedEvent(event) {
|
|
11901
13247
|
return {
|
|
@@ -11959,7 +13305,7 @@ async function parseZedSessionFile(dbPath, options) {
|
|
|
11959
13305
|
const folderRaw = row.folder_paths || "";
|
|
11960
13306
|
const folder = folderRaw.split(/[\n,]/).map((s) => s.trim()).find(Boolean);
|
|
11961
13307
|
const cwd = folder || void 0;
|
|
11962
|
-
const project = cwd ?
|
|
13308
|
+
const project = cwd ? path23.basename(cwd) : row.summary ? row.summary.slice(0, 40) : void 0;
|
|
11963
13309
|
let json;
|
|
11964
13310
|
try {
|
|
11965
13311
|
const bytes = row.data_type === "zstd" ? decompress2(new Uint8Array(row.data)) : new Uint8Array(row.data);
|
|
@@ -12204,20 +13550,20 @@ async function parseZedSessionFile(dbPath, options) {
|
|
|
12204
13550
|
}
|
|
12205
13551
|
return events.filter((event) => matchesBackfillFilters(event, options));
|
|
12206
13552
|
}
|
|
12207
|
-
async function zedBackfillFiles(sourceRoot, home =
|
|
12208
|
-
const { stat:
|
|
13553
|
+
async function zedBackfillFiles(sourceRoot, home = os12.homedir(), env) {
|
|
13554
|
+
const { stat: stat17 } = await import("node:fs/promises");
|
|
12209
13555
|
if (sourceRoot) {
|
|
12210
13556
|
if (!sourceRoot.endsWith(".db")) {
|
|
12211
13557
|
return [];
|
|
12212
13558
|
}
|
|
12213
|
-
const info = await
|
|
13559
|
+
const info = await stat17(sourceRoot).catch(() => null);
|
|
12214
13560
|
if (!info) {
|
|
12215
13561
|
return [];
|
|
12216
13562
|
}
|
|
12217
13563
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
12218
13564
|
}
|
|
12219
13565
|
for (const candidatePath of zedThreadsCandidates(home, env)) {
|
|
12220
|
-
const info = await
|
|
13566
|
+
const info = await stat17(candidatePath).catch(() => null);
|
|
12221
13567
|
if (info) {
|
|
12222
13568
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
12223
13569
|
}
|
|
@@ -12234,7 +13580,7 @@ function createZedAdapter() {
|
|
|
12234
13580
|
return zedConfigDir(home, env);
|
|
12235
13581
|
},
|
|
12236
13582
|
installedPath(home, env) {
|
|
12237
|
-
return
|
|
13583
|
+
return path23.join(zedConfigDir(home, env), "vibetime-marker");
|
|
12238
13584
|
},
|
|
12239
13585
|
async isInstalled() {
|
|
12240
13586
|
return false;
|
|
@@ -12609,6 +13955,10 @@ async function installEntry(entry, options) {
|
|
|
12609
13955
|
await mergeHooksJson(entry.path, entry.content, options);
|
|
12610
13956
|
return;
|
|
12611
13957
|
}
|
|
13958
|
+
if (entry.kind === "cursor-hooks-json" && typeof entry.content === "object") {
|
|
13959
|
+
await mergeCursorHooksFile(entry.path, entry.content, options);
|
|
13960
|
+
return;
|
|
13961
|
+
}
|
|
12612
13962
|
if (entry.kind === "hooks-toml" && typeof entry.content === "object") {
|
|
12613
13963
|
await mergeHooksToml(entry.path, entry.content, options);
|
|
12614
13964
|
return;
|
|
@@ -12623,6 +13973,10 @@ async function uninstallEntry(entry, options) {
|
|
|
12623
13973
|
await uninstallHooksToml(entry.path, entry.content, options);
|
|
12624
13974
|
return;
|
|
12625
13975
|
}
|
|
13976
|
+
if (entry.kind === "cursor-hooks-json" && typeof entry.content === "object") {
|
|
13977
|
+
await uninstallCursorHooksFile(entry.path, entry.content, options);
|
|
13978
|
+
return;
|
|
13979
|
+
}
|
|
12626
13980
|
if (entry.kind === "hooks-json" && typeof entry.content === "object") {
|
|
12627
13981
|
await uninstallHooksJson(entry.path, entry.content, options);
|
|
12628
13982
|
return;
|
|
@@ -12655,6 +14009,67 @@ async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
|
|
|
12655
14009
|
await writeFile5(filePath, nextText, "utf8");
|
|
12656
14010
|
onWrite(`Installed ${filePath}`);
|
|
12657
14011
|
}
|
|
14012
|
+
async function mergeCursorHooksFile(filePath, content, { dryRun, force, onWrite }) {
|
|
14013
|
+
const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
|
|
14014
|
+
const pathMod = await import("node:path");
|
|
14015
|
+
if (dryRun) {
|
|
14016
|
+
onWrite(`Would merge ${filePath}`);
|
|
14017
|
+
return;
|
|
14018
|
+
}
|
|
14019
|
+
const existingText = await readTextIfExists(filePath);
|
|
14020
|
+
const existing = existingText ? JSON.parse(existingText) : {};
|
|
14021
|
+
if (existingText !== null && !isPlainObject(existing) && !force) {
|
|
14022
|
+
throw new Error(
|
|
14023
|
+
`Refusing to update non-object JSON file: ${filePath}. Re-run with --force if this is intentional.`
|
|
14024
|
+
);
|
|
14025
|
+
}
|
|
14026
|
+
const merged = mergeCursorHooksJson(existing, content);
|
|
14027
|
+
const nextText = `${JSON.stringify(merged, null, 2)}
|
|
14028
|
+
`;
|
|
14029
|
+
if (existingText === nextText) {
|
|
14030
|
+
onWrite(`Already installed ${filePath}`);
|
|
14031
|
+
return;
|
|
14032
|
+
}
|
|
14033
|
+
await mkdir6(pathMod.dirname(filePath), { recursive: true });
|
|
14034
|
+
await writeFile5(filePath, nextText, "utf8");
|
|
14035
|
+
onWrite(`Installed ${filePath}`);
|
|
14036
|
+
}
|
|
14037
|
+
async function uninstallCursorHooksFile(filePath, content, { dryRun, onWrite }) {
|
|
14038
|
+
const existingText = await readTextIfExists(filePath);
|
|
14039
|
+
if (existingText === null) {
|
|
14040
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
14041
|
+
return;
|
|
14042
|
+
}
|
|
14043
|
+
let existing;
|
|
14044
|
+
try {
|
|
14045
|
+
existing = JSON.parse(existingText);
|
|
14046
|
+
} catch {
|
|
14047
|
+
onWrite(`Skipped non-JSON file ${filePath}`);
|
|
14048
|
+
return;
|
|
14049
|
+
}
|
|
14050
|
+
if (!isPlainObject(existing)) {
|
|
14051
|
+
onWrite(`Skipped non-object JSON file ${filePath}`);
|
|
14052
|
+
return;
|
|
14053
|
+
}
|
|
14054
|
+
const commands = new Set(collectCursorHookCommands(content));
|
|
14055
|
+
if (commands.size === 0) {
|
|
14056
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
14057
|
+
return;
|
|
14058
|
+
}
|
|
14059
|
+
const { next, changed } = stripCursorHookCommands(existing, commands);
|
|
14060
|
+
if (!changed) {
|
|
14061
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
14062
|
+
return;
|
|
14063
|
+
}
|
|
14064
|
+
if (dryRun) {
|
|
14065
|
+
onWrite(`Would uninstall ${filePath}`);
|
|
14066
|
+
return;
|
|
14067
|
+
}
|
|
14068
|
+
const { writeFile: writeFile5 } = await import("node:fs/promises");
|
|
14069
|
+
await writeFile5(filePath, `${JSON.stringify(next, null, 2)}
|
|
14070
|
+
`, "utf8");
|
|
14071
|
+
onWrite(`Uninstalled ${filePath}`);
|
|
14072
|
+
}
|
|
12658
14073
|
function mergeAgyHooksJson(existing, addition) {
|
|
12659
14074
|
const merged = structuredClone(isPlainObject(existing) ? existing : {});
|
|
12660
14075
|
const additionObject = isPlainObject(addition) ? addition : {};
|
|
@@ -12942,15 +14357,15 @@ async function uninstallGeneratedFile(filePath, { dryRun, onWrite }) {
|
|
|
12942
14357
|
import { randomUUID } from "node:crypto";
|
|
12943
14358
|
import { existsSync as existsSync2, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
12944
14359
|
import { homedir, hostname } from "node:os";
|
|
12945
|
-
import
|
|
14360
|
+
import path24 from "node:path";
|
|
12946
14361
|
function configDir(home = homedir()) {
|
|
12947
|
-
return
|
|
14362
|
+
return path24.join(home, ".vibetime");
|
|
12948
14363
|
}
|
|
12949
14364
|
function configPath(home = homedir()) {
|
|
12950
|
-
return
|
|
14365
|
+
return path24.join(configDir(home), "config.json");
|
|
12951
14366
|
}
|
|
12952
14367
|
function machineIdPath(home = homedir()) {
|
|
12953
|
-
return
|
|
14368
|
+
return path24.join(configDir(home), "machine-id");
|
|
12954
14369
|
}
|
|
12955
14370
|
function readConfig(home = homedir()) {
|
|
12956
14371
|
const file = configPath(home);
|
|
@@ -12997,15 +14412,15 @@ function defaultMachineName() {
|
|
|
12997
14412
|
init_fs();
|
|
12998
14413
|
|
|
12999
14414
|
// src/lib/logger.ts
|
|
13000
|
-
import { appendFile, mkdir as mkdir4, rename, stat as
|
|
14415
|
+
import { appendFile, mkdir as mkdir4, rename, stat as stat15 } from "node:fs/promises";
|
|
13001
14416
|
import { homedir as homedir2 } from "node:os";
|
|
13002
|
-
import
|
|
14417
|
+
import path25 from "node:path";
|
|
13003
14418
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
13004
14419
|
function logDir(home = homedir2()) {
|
|
13005
|
-
return
|
|
14420
|
+
return path25.join(home, ".vibetime", "logs");
|
|
13006
14421
|
}
|
|
13007
14422
|
function logPath(home = homedir2(), name = "cli.log") {
|
|
13008
|
-
return
|
|
14423
|
+
return path25.join(logDir(home), name);
|
|
13009
14424
|
}
|
|
13010
14425
|
function serializeError(error) {
|
|
13011
14426
|
if (error instanceof Error) {
|
|
@@ -13015,7 +14430,7 @@ function serializeError(error) {
|
|
|
13015
14430
|
}
|
|
13016
14431
|
async function rotateIfNeeded(file) {
|
|
13017
14432
|
try {
|
|
13018
|
-
const info = await
|
|
14433
|
+
const info = await stat15(file);
|
|
13019
14434
|
if (info.size > MAX_BYTES) {
|
|
13020
14435
|
await rename(file, `${file}.1`).catch(() => {
|
|
13021
14436
|
});
|
|
@@ -13180,8 +14595,8 @@ function buildHeaders(token, machine) {
|
|
|
13180
14595
|
...machine?.platform ? { "x-machine-platform": machine.platform } : {}
|
|
13181
14596
|
};
|
|
13182
14597
|
}
|
|
13183
|
-
function joinUrl(base,
|
|
13184
|
-
return new URL(
|
|
14598
|
+
function joinUrl(base, path27) {
|
|
14599
|
+
return new URL(path27, base.endsWith("/") ? base : `${base}/`).toString();
|
|
13185
14600
|
}
|
|
13186
14601
|
async function postRollupBatch(remote, rollups, options = {}) {
|
|
13187
14602
|
const response = await remote.fetchImpl(joinUrl(remote.baseUrl, "/v3/agent/ingest"), {
|
|
@@ -13271,6 +14686,7 @@ function createRegistry() {
|
|
|
13271
14686
|
registry.register(createGrokBuildAdapter());
|
|
13272
14687
|
registry.register(createZedAdapter());
|
|
13273
14688
|
registry.register(createKimiCodeAdapter());
|
|
14689
|
+
registry.register(createCursorAdapter());
|
|
13274
14690
|
return registry;
|
|
13275
14691
|
}
|
|
13276
14692
|
var defaultContext = {
|
|
@@ -13828,6 +15244,9 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
13828
15244
|
if (source.id === "zed") {
|
|
13829
15245
|
return zedBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
13830
15246
|
}
|
|
15247
|
+
if (source.id === "cursor") {
|
|
15248
|
+
return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
15249
|
+
}
|
|
13831
15250
|
if (source.id === "pi") {
|
|
13832
15251
|
return piBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
|
|
13833
15252
|
}
|
|
@@ -13835,7 +15254,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
13835
15254
|
const fileLists = await Promise.all(roots.map((r) => listJsonlFiles(r)));
|
|
13836
15255
|
const files = fileLists.flat().sort().slice(0, numberOption(options.limit) || void 0);
|
|
13837
15256
|
return Promise.all(files.map(async (filePath) => {
|
|
13838
|
-
const info = await
|
|
15257
|
+
const info = await stat16(filePath);
|
|
13839
15258
|
return { path: filePath, modifiedAt: info.mtime.toISOString() };
|
|
13840
15259
|
}));
|
|
13841
15260
|
}
|
|
@@ -14167,13 +15586,13 @@ function selectBackfillFilesForImport(files, watermarkTs) {
|
|
|
14167
15586
|
return picked;
|
|
14168
15587
|
}
|
|
14169
15588
|
function backfillIncrementalStatePath(home) {
|
|
14170
|
-
return
|
|
15589
|
+
return path26.join(home, ".vibetime", "backfill-state.json");
|
|
14171
15590
|
}
|
|
14172
15591
|
function syncLocalTriggerStatePath(home) {
|
|
14173
|
-
return
|
|
15592
|
+
return path26.join(home, ".vibetime", "sync-local-trigger.json");
|
|
14174
15593
|
}
|
|
14175
15594
|
function syncLocalTriggerLockPath(home) {
|
|
14176
|
-
return
|
|
15595
|
+
return path26.join(home, ".vibetime", "sync-local-trigger.lock");
|
|
14177
15596
|
}
|
|
14178
15597
|
function backfillRemoteKey(baseUrl) {
|
|
14179
15598
|
try {
|
|
@@ -14235,7 +15654,7 @@ async function readBackfillIncrementalStateFile(home, ctx) {
|
|
|
14235
15654
|
}
|
|
14236
15655
|
async function writeBackfillIncrementalStateFile(home, file) {
|
|
14237
15656
|
const statePath = backfillIncrementalStatePath(home);
|
|
14238
|
-
await mkdir5(
|
|
15657
|
+
await mkdir5(path26.dirname(statePath), { recursive: true });
|
|
14239
15658
|
await writeFile4(statePath, `${JSON.stringify(file, null, 2)}
|
|
14240
15659
|
`, "utf8");
|
|
14241
15660
|
}
|
|
@@ -14284,7 +15703,7 @@ async function readSyncLocalTriggerState(statePath) {
|
|
|
14284
15703
|
return nextState;
|
|
14285
15704
|
}
|
|
14286
15705
|
async function writeSyncLocalTriggerState(statePath, state) {
|
|
14287
|
-
await mkdir5(
|
|
15706
|
+
await mkdir5(path26.dirname(statePath), { recursive: true });
|
|
14288
15707
|
await writeFile4(statePath, `${JSON.stringify(state, null, 2)}
|
|
14289
15708
|
`, "utf8");
|
|
14290
15709
|
}
|
|
@@ -14299,12 +15718,12 @@ async function readSyncLocalLock(lockPath) {
|
|
|
14299
15718
|
return { pid: lock.pid, startedAt: lock.startedAt };
|
|
14300
15719
|
}
|
|
14301
15720
|
async function writeSyncLocalLock(lockPath, lock) {
|
|
14302
|
-
await mkdir5(
|
|
15721
|
+
await mkdir5(path26.dirname(lockPath), { recursive: true });
|
|
14303
15722
|
await writeFile4(lockPath, `${JSON.stringify(lock, null, 2)}
|
|
14304
15723
|
`, "utf8");
|
|
14305
15724
|
}
|
|
14306
15725
|
async function acquireSyncLocalLock(lockPath, lock) {
|
|
14307
|
-
await mkdir5(
|
|
15726
|
+
await mkdir5(path26.dirname(lockPath), { recursive: true });
|
|
14308
15727
|
try {
|
|
14309
15728
|
const handle = await open(lockPath, "wx");
|
|
14310
15729
|
try {
|
|
@@ -14384,10 +15803,10 @@ function syncLocalRunnerEntryArgs(cliPath) {
|
|
|
14384
15803
|
if (cliPath.endsWith(".ts")) {
|
|
14385
15804
|
return ["--import", "tsx", cliPath];
|
|
14386
15805
|
}
|
|
14387
|
-
return [
|
|
15806
|
+
return [path26.resolve(path26.dirname(cliPath), "../bin/vibetime.mjs")];
|
|
14388
15807
|
}
|
|
14389
15808
|
function resolveHome3(options, ctx) {
|
|
14390
|
-
return
|
|
15809
|
+
return path26.resolve(stringOption(options.home) || ctx.env.HOME || os13.homedir());
|
|
14391
15810
|
}
|
|
14392
15811
|
function requestedTargets(options) {
|
|
14393
15812
|
const value = options.target || options.targets;
|