@yhong91/vibetime 0.1.55 → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/vibetime.mjs +1656 -331
  2. 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 stat14, writeFile as writeFile4 } from "node:fs/promises";
888
- import os12 from "node:os";
889
- import path25 from "node:path";
887
+ import { mkdir as mkdir5, open, rm, stat as stat15, 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.55" : "0.1.1";
2050
+ var PACKAGE_VERSION = true ? "0.1.57" : "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 stat15 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
5015
- files.push({ path: filePath, modifiedAt: stat15.mtime.toISOString(), groupId: pidDir.name });
5014
+ const stat16 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
5015
+ files.push({ path: filePath, modifiedAt: stat16.mtime.toISOString(), groupId: pidDir.name });
5016
5016
  } catch {
5017
5017
  }
5018
5018
  }
@@ -6177,86 +6177,1292 @@ async function copilotBackfillFiles(sourceRoot, home = os5.homedir(), _env) {
6177
6177
  } catch {
6178
6178
  return [];
6179
6179
  }
6180
- for (const entry of entries) {
6181
- if (entry.startsWith("pending-session")) {
6180
+ for (const entry of entries) {
6181
+ if (entry.startsWith("pending-session")) {
6182
+ continue;
6183
+ }
6184
+ const eventsPath = path12.join(sessionDir, entry, "events.jsonl");
6185
+ const info = await stat6(eventsPath).catch(() => null);
6186
+ if (info) {
6187
+ results.push({ path: eventsPath, modifiedAt: info.mtime.toISOString() });
6188
+ }
6189
+ }
6190
+ return results;
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
+ `;
6198
+ }
6199
+ function copilotHome(home, env) {
6200
+ const override = env?.COPILOT_HOME;
6201
+ if (override && override.trim()) {
6202
+ return path12.resolve(override);
6203
+ }
6204
+ return path12.join(home, ".copilot");
6205
+ }
6206
+ function createCopilotAdapter() {
6207
+ return {
6208
+ id: "copilot",
6209
+ label: "GitHub Copilot",
6210
+ agentName: "copilot",
6211
+ kind: "agent",
6212
+ detectPath(home, env) {
6213
+ return copilotHome(home, env);
6214
+ },
6215
+ installedPath(home, env) {
6216
+ return path12.join(copilotHome(home, env), ".vibetime");
6217
+ },
6218
+ async isInstalled(home, env) {
6219
+ try {
6220
+ const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
6221
+ return await pathExists2(path12.join(copilotHome(home, env), ".vibetime"));
6222
+ } catch {
6223
+ return false;
6224
+ }
6225
+ },
6226
+ installEntries(home, env) {
6227
+ return [{
6228
+ kind: "file",
6229
+ path: path12.join(copilotHome(home, env), ".vibetime"),
6230
+ content: copilotPluginContent()
6231
+ }];
6232
+ },
6233
+ sourcePaths(home, env) {
6234
+ return [path12.join(copilotHome(home, env), "session-state")];
6235
+ },
6236
+ parseSessionFile: parseCopilotSessionFile
6237
+ };
6238
+ }
6239
+
6240
+ // src/adapters/cursor.ts
6241
+ import { copyFile, readdir as readdir6, readFile as readFile8, stat as stat7 } from "node:fs/promises";
6242
+ import os6 from "node:os";
6243
+ import path13 from "node:path";
6244
+
6245
+ // src/lib/cursor-hooks.ts
6246
+ function isCursorHooksFile(value) {
6247
+ if (!isPlainObject(value)) {
6248
+ return false;
6249
+ }
6250
+ if (value.version !== 1 && value.version !== void 0) {
6251
+ return false;
6252
+ }
6253
+ return isPlainObject(value.hooks);
6254
+ }
6255
+ function hookCommandFromCursorEntry(entry) {
6256
+ if (!isPlainObject(entry) || typeof entry.command !== "string" || !entry.command) {
6257
+ return void 0;
6258
+ }
6259
+ return entry.command;
6260
+ }
6261
+ function hasCursorHookCommand(config, command) {
6262
+ if (!isCursorHooksFile(config)) {
6263
+ return false;
6264
+ }
6265
+ return Object.values(config.hooks).some(
6266
+ (entries) => Array.isArray(entries) && entries.some((entry) => hookCommandFromCursorEntry(entry) === command)
6267
+ );
6268
+ }
6269
+ function mergeCursorHooksJson(existing, addition) {
6270
+ const mergedHooks = {};
6271
+ const existingHooks = isCursorHooksFile(existing) ? existing.hooks : {};
6272
+ for (const [event, entries] of Object.entries(existingHooks)) {
6273
+ if (Array.isArray(entries)) {
6274
+ mergedHooks[event] = [...entries];
6275
+ }
6276
+ }
6277
+ for (const [event, entries] of Object.entries(addition.hooks)) {
6278
+ const current = mergedHooks[event] ? [...mergedHooks[event]] : [];
6279
+ for (const entry of entries) {
6280
+ const command = hookCommandFromCursorEntry(entry);
6281
+ if (!command) {
6282
+ continue;
6283
+ }
6284
+ const alreadyPresent = current.some(
6285
+ (existingEntry) => hookCommandFromCursorEntry(existingEntry) === command
6286
+ );
6287
+ if (!alreadyPresent) {
6288
+ current.push(entry);
6289
+ }
6290
+ }
6291
+ mergedHooks[event] = current;
6292
+ }
6293
+ const extra = isPlainObject(existing) ? Object.fromEntries(
6294
+ Object.entries(existing).filter(([key]) => key !== "version" && key !== "hooks")
6295
+ ) : {};
6296
+ return {
6297
+ ...extra,
6298
+ version: 1,
6299
+ hooks: mergedHooks
6300
+ };
6301
+ }
6302
+ function stripCursorHookCommands(existing, commands) {
6303
+ const next = structuredClone(isPlainObject(existing) ? existing : {});
6304
+ if (!isPlainObject(next.hooks)) {
6305
+ return { next, changed: false };
6306
+ }
6307
+ const hooks = { ...next.hooks };
6308
+ let changed = false;
6309
+ for (const [event, entries] of Object.entries(hooks)) {
6310
+ if (!Array.isArray(entries)) {
6311
+ continue;
6312
+ }
6313
+ const stripped = entries.filter((entry) => {
6314
+ const command = hookCommandFromCursorEntry(entry);
6315
+ return !command || !commands.has(command);
6316
+ });
6317
+ if (stripped.length !== entries.length) {
6318
+ changed = true;
6319
+ }
6320
+ if (stripped.length === 0) {
6321
+ delete hooks[event];
6322
+ } else {
6323
+ hooks[event] = stripped;
6324
+ }
6325
+ }
6326
+ if (Object.keys(hooks).length === 0) {
6327
+ delete next.hooks;
6328
+ changed = true;
6329
+ } else {
6330
+ next.hooks = hooks;
6331
+ }
6332
+ return { next, changed };
6333
+ }
6334
+ function collectCursorHookCommands(content) {
6335
+ const commands = /* @__PURE__ */ new Set();
6336
+ for (const entries of Object.values(content.hooks)) {
6337
+ for (const entry of entries) {
6338
+ const command = hookCommandFromCursorEntry(entry);
6339
+ if (command) {
6340
+ commands.add(command);
6341
+ }
6342
+ }
6343
+ }
6344
+ return [...commands];
6345
+ }
6346
+
6347
+ // src/adapters/cursor.ts
6348
+ init_fs();
6349
+ var SOURCE_ID = "cursor";
6350
+ var AGENT_NAME = "cursor";
6351
+ var HOOK_COMMAND = `vibetime hook --agent ${SOURCE_ID}`;
6352
+ var HOOK_TIMEOUT_SECONDS = 10;
6353
+ var BUBBLE_USER = 1;
6354
+ var CAPABILITY_TOOL = 15;
6355
+ var CURSOR_HOOK_EVENTS = [
6356
+ "sessionStart",
6357
+ "sessionEnd",
6358
+ "beforeSubmitPrompt",
6359
+ "preToolUse",
6360
+ "postToolUse",
6361
+ "postToolUseFailure",
6362
+ "subagentStart",
6363
+ "subagentStop",
6364
+ "stop",
6365
+ "afterFileEdit",
6366
+ "afterShellExecution",
6367
+ "preCompact"
6368
+ ];
6369
+ function cursorHome(home, env) {
6370
+ const override = env?.CURSOR_HOME;
6371
+ if (override && override.trim()) {
6372
+ return path13.resolve(override);
6373
+ }
6374
+ return path13.join(home, ".cursor");
6375
+ }
6376
+ function cursorHooksPath(home, env) {
6377
+ return path13.join(cursorHome(home, env), "hooks.json");
6378
+ }
6379
+ function cursorProjectsDir(home, env) {
6380
+ return path13.join(cursorHome(home, env), "projects");
6381
+ }
6382
+ function cursorChatsDir(home, env) {
6383
+ return path13.join(cursorHome(home, env), "chats");
6384
+ }
6385
+ function cursorUserDir(home, env) {
6386
+ const override = env?.CURSOR_USER_DIR;
6387
+ if (override && override.trim()) {
6388
+ return path13.resolve(override);
6389
+ }
6390
+ return void 0;
6391
+ }
6392
+ function cursorStateDbCandidates(home, env) {
6393
+ const candidates = [];
6394
+ const userDir = cursorUserDir(home, env);
6395
+ if (userDir) {
6396
+ candidates.push(path13.join(userDir, "globalStorage", "state.vscdb"));
6397
+ }
6398
+ const platform2 = process.platform;
6399
+ if (platform2 === "darwin") {
6400
+ candidates.push(path13.join(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb"));
6401
+ } else if (platform2 === "win32") {
6402
+ const appdata = env?.APPDATA;
6403
+ if (appdata && appdata.trim()) {
6404
+ candidates.push(path13.join(path13.resolve(appdata), "Cursor", "User", "globalStorage", "state.vscdb"));
6405
+ }
6406
+ candidates.push(path13.join(home, "AppData", "Roaming", "Cursor", "User", "globalStorage", "state.vscdb"));
6407
+ } else {
6408
+ const xdgConfig = env?.XDG_CONFIG_HOME;
6409
+ if (xdgConfig && xdgConfig.trim()) {
6410
+ candidates.push(path13.join(path13.resolve(xdgConfig), "Cursor", "User", "globalStorage", "state.vscdb"));
6411
+ }
6412
+ candidates.push(path13.join(home, ".config", "Cursor", "User", "globalStorage", "state.vscdb"));
6413
+ }
6414
+ return candidates;
6415
+ }
6416
+ function decodeKv(value) {
6417
+ if (typeof value === "string") {
6418
+ return value;
6419
+ }
6420
+ if (value instanceof Uint8Array) {
6421
+ return new TextDecoder().decode(value);
6422
+ }
6423
+ return void 0;
6424
+ }
6425
+ function parseJsonObject(text) {
6426
+ if (!text) {
6427
+ return void 0;
6428
+ }
6429
+ try {
6430
+ const parsed = JSON.parse(text);
6431
+ return isPlainObject(parsed) ? parsed : void 0;
6432
+ } catch {
6433
+ return void 0;
6434
+ }
6435
+ }
6436
+ function parseMaybeJsonObject(value) {
6437
+ if (isPlainObject(value)) {
6438
+ return value;
6439
+ }
6440
+ if (typeof value === "string") {
6441
+ return parseJsonObject(value) || {};
6442
+ }
6443
+ return {};
6444
+ }
6445
+ function cleanId(value) {
6446
+ return value.replace(/\s+/g, "_").slice(0, 200);
6447
+ }
6448
+ async function openCursorDb(dbPath) {
6449
+ const { DatabaseSync } = await import("node:sqlite");
6450
+ try {
6451
+ return {
6452
+ db: new DatabaseSync(dbPath, { readOnly: true }),
6453
+ cleanup: async () => {
6454
+ }
6455
+ };
6456
+ } catch {
6457
+ const tmp = path13.join(os6.tmpdir(), `vibetime-cursor-${createStableHash(dbPath).slice(0, 12)}.db`);
6458
+ await copyFile(dbPath, tmp);
6459
+ for (const suffix of ["-wal", "-shm"]) {
6460
+ await copyFile(`${dbPath}${suffix}`, `${tmp}${suffix}`).catch(() => void 0);
6461
+ }
6462
+ return {
6463
+ db: new DatabaseSync(tmp, { readOnly: true }),
6464
+ cleanup: async () => {
6465
+ const { unlink } = await import("node:fs/promises");
6466
+ await unlink(tmp).catch(() => void 0);
6467
+ await unlink(`${tmp}-wal`).catch(() => void 0);
6468
+ await unlink(`${tmp}-shm`).catch(() => void 0);
6469
+ }
6470
+ };
6471
+ }
6472
+ }
6473
+ function listComposers(db) {
6474
+ const dataById = /* @__PURE__ */ new Map();
6475
+ const dataRows = db.prepare(
6476
+ "SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'"
6477
+ ).all();
6478
+ for (const row of dataRows) {
6479
+ const id = row.key.slice("composerData:".length);
6480
+ const data = parseJsonObject(decodeKv(row.value));
6481
+ if (id && data) {
6482
+ dataById.set(id, data);
6483
+ }
6484
+ }
6485
+ let headers = [];
6486
+ try {
6487
+ headers = db.prepare(
6488
+ "SELECT composerId, createdAt, lastUpdatedAt, isSubagent, value FROM composerHeaders"
6489
+ ).all();
6490
+ } catch {
6491
+ headers = [...dataById.entries()].map(([composerId, data]) => ({
6492
+ composerId,
6493
+ createdAt: numberField(data, "createdAt"),
6494
+ lastUpdatedAt: numberField(data, "lastUpdatedAt"),
6495
+ isSubagent: data.isBestOfNSubcomposer || data.isSubagent ? 1 : 0,
6496
+ value: JSON.stringify(data)
6497
+ }));
6498
+ }
6499
+ const composers = [];
6500
+ for (const headerRow of headers) {
6501
+ const composerId = headerRow.composerId;
6502
+ if (!composerId || composerId === "empty-state-draft") {
6503
+ continue;
6504
+ }
6505
+ const header = parseJsonObject(decodeKv(headerRow.value)) || parseMaybeJsonObject(headerRow.value);
6506
+ const data = dataById.get(composerId) || header;
6507
+ if (data.isDraft === true || data.isEphemeral === true || header.isDraft === true || header.isEphemeral === true) {
6508
+ continue;
6509
+ }
6510
+ composers.push({
6511
+ composerId,
6512
+ createdAt: headerRow.createdAt ?? numberField(data, "createdAt") ?? numberField(header, "createdAt"),
6513
+ lastUpdatedAt: headerRow.lastUpdatedAt ?? numberField(data, "lastUpdatedAt") ?? numberField(header, "lastUpdatedAt"),
6514
+ isSubagent: Boolean(headerRow.isSubagent) || data.isBestOfNSubcomposer === true,
6515
+ header,
6516
+ data
6517
+ });
6518
+ }
6519
+ return composers;
6520
+ }
6521
+ function loadBubbles(db, composerId) {
6522
+ const rows = db.prepare(
6523
+ "SELECT key, value FROM cursorDiskKV WHERE key LIKE ?"
6524
+ ).all(`bubbleId:${composerId}:%`);
6525
+ const bubbles = [];
6526
+ for (const row of rows) {
6527
+ const bubble = parseJsonObject(decodeKv(row.value));
6528
+ if (bubble) {
6529
+ bubbles.push(bubble);
6530
+ }
6531
+ }
6532
+ bubbles.sort((a, b) => {
6533
+ const aTs = stringField(a, "createdAt") || "";
6534
+ const bTs = stringField(b, "createdAt") || "";
6535
+ return aTs.localeCompare(bTs);
6536
+ });
6537
+ return bubbles;
6538
+ }
6539
+ function workspaceFromComposer(data) {
6540
+ const ident = objectField(data, "workspaceIdentifier");
6541
+ const uri = objectField(ident, "uri");
6542
+ const cwd = stringField(uri, "fsPath") || stringField(uri, "path");
6543
+ return {
6544
+ cwd,
6545
+ project: cwd ? path13.basename(cwd) : void 0
6546
+ };
6547
+ }
6548
+ function modelFromComposer(data) {
6549
+ const config = objectField(data, "modelConfig");
6550
+ return stringField(config, "modelName") || stringField(config, "modelId");
6551
+ }
6552
+ function bubbleTs(bubble, fallback) {
6553
+ return timestampFrom(bubble.createdAt) || stringField(bubble, "createdAt") || fallback;
6554
+ }
6555
+ function toolNameOf(raw) {
6556
+ if (!raw) {
6557
+ return "unknown";
6558
+ }
6559
+ return raw.replace(/_v\d+$/i, "");
6560
+ }
6561
+ function isShellTool(name) {
6562
+ const lower = name.toLowerCase();
6563
+ return lower === "shell" || lower === "bash" || lower === "run_terminal_cmd" || lower === "run_terminal_command";
6564
+ }
6565
+ function fileActivitiesFromCursorTool(toolName, input, ts, cwd) {
6566
+ const lower = toolName.toLowerCase();
6567
+ if (isShellTool(toolName)) {
6568
+ const command = stringField(input, "command") || stringField(input, "cmd");
6569
+ if (command) {
6570
+ return fileActivitiesFromShellCommand(command, ts, cwd, cwd);
6571
+ }
6572
+ return [];
6573
+ }
6574
+ const target = stringField(input, "path") || stringField(input, "targetFile") || stringField(input, "target_file") || stringField(input, "file_path") || stringField(input, "filePath");
6575
+ const display = target ? displayFilePath(target, cwd) : void 0;
6576
+ if (lower === "read" || lower === "read_file" || lower === "readfile") {
6577
+ if (!display) {
6578
+ return [];
6579
+ }
6580
+ return [{ ts, path: display, operation: "read", confidence: "exact" }];
6581
+ }
6582
+ if (lower === "grep" || lower === "grep_search" || lower === "ripgrep" || lower === "glob" || lower === "glob_file_search" || lower === "globfilesearch") {
6583
+ if (!display) {
6584
+ return [];
6585
+ }
6586
+ return [{ ts, path: display, operation: "search", confidence: "exact" }];
6587
+ }
6588
+ if (lower === "delete" || lower === "delete_file") {
6589
+ if (!display) {
6590
+ return [];
6591
+ }
6592
+ return [{ ts, path: display, operation: "delete", confidence: "exact" }];
6593
+ }
6594
+ if (lower === "write" || lower === "write_file") {
6595
+ if (!display) {
6596
+ return [];
6597
+ }
6598
+ const content = stringField(input, "contents") || stringField(input, "content");
6599
+ return [{
6600
+ ts,
6601
+ path: display,
6602
+ operation: "write",
6603
+ confidence: "exact",
6604
+ linesAdded: countTextLines(content),
6605
+ charsWritten: content?.length
6606
+ }];
6607
+ }
6608
+ if (lower === "search_replace" || lower === "strreplace" || lower === "edit" || lower === "edit_file" || lower === "apply_patch" || lower === "applypatch") {
6609
+ if (!display) {
6610
+ return [];
6611
+ }
6612
+ const oldString = stringField(input, "old_string") || stringField(input, "oldString") || stringField(input, "old_str");
6613
+ const newString = stringField(input, "new_string") || stringField(input, "newString") || stringField(input, "new_str") || stringField(input, "contents") || stringField(input, "content");
6614
+ return [{
6615
+ ts,
6616
+ path: display,
6617
+ operation: "edit",
6618
+ confidence: "exact",
6619
+ linesAdded: countTextLines(newString),
6620
+ linesRemoved: countTextLines(oldString),
6621
+ charsWritten: newString?.length
6622
+ }];
6623
+ }
6624
+ return [];
6625
+ }
6626
+ function usageMetrics(tokenCount) {
6627
+ const inputTokens = numberField(tokenCount, "inputTokens") || 0;
6628
+ const outputTokens = numberField(tokenCount, "outputTokens") || 0;
6629
+ const cached = numberField(tokenCount, "cacheReadTokens") || numberField(tokenCount, "cachedInputTokens") || 0;
6630
+ const cacheCreation = numberField(tokenCount, "cacheWriteTokens") || numberField(tokenCount, "cacheCreationTokens") || 0;
6631
+ if (inputTokens <= 0 && outputTokens <= 0 && cached <= 0 && cacheCreation <= 0) {
6632
+ return void 0;
6633
+ }
6634
+ return {
6635
+ tokensInput: inputTokens || void 0,
6636
+ tokensOutput: outputTokens || void 0,
6637
+ tokensCachedInput: cached + cacheCreation || void 0,
6638
+ tokensCacheReadInput: cached || void 0,
6639
+ tokensCacheCreationInput: cacheCreation || void 0,
6640
+ tokensTotal: inputTokens + outputTokens || void 0,
6641
+ modelCalls: 1
6642
+ };
6643
+ }
6644
+ var CURSOR_TIMESTAMP_RE = /<timestamp>\s*([^<]+?)\s*<\/timestamp>/i;
6645
+ var CURSOR_USER_QUERY_RE = /<user_query>\s*([\s\S]*?)\s*<\/user_query>/i;
6646
+ var chatMetaIndexCache = /* @__PURE__ */ new Map();
6647
+ function isCursorTranscriptPath(filePath) {
6648
+ return filePath.split(path13.sep).includes("agent-transcripts") && filePath.endsWith(".jsonl");
6649
+ }
6650
+ function cursorHomeFromTranscript(filePath) {
6651
+ const parts = filePath.split(path13.sep);
6652
+ const at = parts.lastIndexOf("agent-transcripts");
6653
+ if (at >= 2 && parts[at - 2] === "projects") {
6654
+ return parts.slice(0, at - 2).join(path13.sep);
6655
+ }
6656
+ return void 0;
6657
+ }
6658
+ function parseCursorClock(raw) {
6659
+ const trimmed = raw.trim();
6660
+ const direct = timestampFrom(trimmed);
6661
+ if (direct) {
6662
+ return direct;
6663
+ }
6664
+ const withoutWeekday = trimmed.replace(/^[A-Za-z]{3,9},\s*/, "");
6665
+ const normalized = withoutWeekday.replace(/\((UTC[+-]\d{1,2}(?::\d{2})?)\)/i, "$1");
6666
+ for (const candidate of [normalized, withoutWeekday, trimmed]) {
6667
+ const parsed = Date.parse(candidate);
6668
+ if (!Number.isNaN(parsed)) {
6669
+ return new Date(parsed).toISOString();
6670
+ }
6671
+ }
6672
+ return void 0;
6673
+ }
6674
+ function latestTimestamp(...values) {
6675
+ let best;
6676
+ for (const value of values) {
6677
+ if (!value || Number.isNaN(Date.parse(value))) {
6678
+ continue;
6679
+ }
6680
+ if (!best || value > best) {
6681
+ best = value;
6682
+ }
6683
+ }
6684
+ return best;
6685
+ }
6686
+ function timestampBetween(start, end, index, count) {
6687
+ if (count <= 0 || index < 0) {
6688
+ return start;
6689
+ }
6690
+ const startMs = Date.parse(start);
6691
+ const endMs = Date.parse(end);
6692
+ if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs <= startMs) {
6693
+ return start;
6694
+ }
6695
+ return new Date(startMs + Math.round((endMs - startMs) * (index + 1) / (count + 1))).toISOString();
6696
+ }
6697
+ function parseCursorUserText(text) {
6698
+ const tsMatch = text.match(CURSOR_TIMESTAMP_RE);
6699
+ const queryMatch = text.match(CURSOR_USER_QUERY_RE);
6700
+ return {
6701
+ ts: tsMatch?.[1] ? parseCursorClock(tsMatch[1]) : void 0,
6702
+ prompt: (queryMatch?.[1] || text.replace(CURSOR_TIMESTAMP_RE, "")).trim()
6703
+ };
6704
+ }
6705
+ function messageText(message) {
6706
+ const content = message.content;
6707
+ if (typeof content === "string") {
6708
+ return content;
6709
+ }
6710
+ if (!Array.isArray(content)) {
6711
+ return stringField(message, "text") || "";
6712
+ }
6713
+ const parts = [];
6714
+ for (const item of content) {
6715
+ if (isPlainObject(item) && stringField(item, "type") === "text") {
6716
+ const text = stringField(item, "text");
6717
+ if (text) {
6718
+ parts.push(text);
6719
+ }
6720
+ }
6721
+ }
6722
+ return parts.join("\n");
6723
+ }
6724
+ function messageToolUses(message) {
6725
+ const content = message.content;
6726
+ if (!Array.isArray(content)) {
6727
+ return [];
6728
+ }
6729
+ const tools = [];
6730
+ for (const item of content) {
6731
+ if (!isPlainObject(item) || stringField(item, "type") !== "tool_use") {
6732
+ continue;
6733
+ }
6734
+ tools.push({
6735
+ name: stringField(item, "name") || "unknown",
6736
+ id: stringField(item, "id"),
6737
+ input: isPlainObject(item.input) ? item.input : parseMaybeJsonObject(item.input)
6738
+ });
6739
+ }
6740
+ return tools;
6741
+ }
6742
+ async function indexCursorChatMeta(cursorHome2) {
6743
+ const cached = chatMetaIndexCache.get(cursorHome2);
6744
+ if (cached) {
6745
+ return cached;
6746
+ }
6747
+ const pending = buildCursorChatMetaIndex(cursorHome2);
6748
+ chatMetaIndexCache.set(cursorHome2, pending);
6749
+ return pending;
6750
+ }
6751
+ async function buildCursorChatMetaIndex(cursorHome2) {
6752
+ const index = /* @__PURE__ */ new Map();
6753
+ const chatsDir = path13.join(cursorHome2, "chats");
6754
+ const projectDirs = await readdir6(chatsDir, { withFileTypes: true }).catch(() => []);
6755
+ for (const project of projectDirs) {
6756
+ if (!project.isDirectory()) {
6757
+ continue;
6758
+ }
6759
+ const sessions = await readdir6(path13.join(chatsDir, project.name), { withFileTypes: true }).catch(() => []);
6760
+ for (const session of sessions) {
6761
+ if (!session.isDirectory()) {
6762
+ continue;
6763
+ }
6764
+ const sessionDir = path13.join(chatsDir, project.name, session.name);
6765
+ const meta = await readJsonIfExists(path13.join(sessionDir, "meta.json"));
6766
+ const fromMeta = isPlainObject(meta) ? meta : {};
6767
+ const fromStore = await readCursorStoreMeta(path13.join(sessionDir, "store.db"));
6768
+ const cwd = stringField(fromMeta, "cwd") || fromStore.cwd;
6769
+ index.set(session.name, {
6770
+ cwd,
6771
+ project: cwd ? path13.basename(cwd) : void 0,
6772
+ model: fromStore.model,
6773
+ startedAt: timestampFrom(fromMeta.createdAtMs) || fromStore.startedAt,
6774
+ endedAt: timestampFrom(fromMeta.updatedAtMs) || fromStore.endedAt
6775
+ });
6776
+ }
6777
+ }
6778
+ return index;
6779
+ }
6780
+ async function readCursorStoreMeta(storePath) {
6781
+ const info = await stat7(storePath).catch(() => null);
6782
+ if (!info) {
6783
+ return {};
6784
+ }
6785
+ try {
6786
+ const { DatabaseSync } = await import("node:sqlite");
6787
+ const db = new DatabaseSync(storePath, { readOnly: true });
6788
+ try {
6789
+ const row = db.prepare("SELECT value FROM meta LIMIT 1").get();
6790
+ const decoded = decodeStoreMetaValue(row?.value);
6791
+ if (!decoded) {
6792
+ return {};
6793
+ }
6794
+ const cwd = stringField(decoded, "cwd") || stringField(objectField(decoded, "workspace"), "path");
6795
+ return {
6796
+ cwd,
6797
+ project: cwd ? path13.basename(cwd) : void 0,
6798
+ model: stringField(decoded, "lastUsedModel") || stringField(decoded, "model"),
6799
+ startedAt: timestampFrom(decoded.createdAt),
6800
+ endedAt: timestampFrom(decoded.lastUpdatedAt) || timestampFrom(decoded.updatedAt)
6801
+ };
6802
+ } finally {
6803
+ db.close();
6804
+ }
6805
+ } catch {
6806
+ return {};
6807
+ }
6808
+ }
6809
+ function decodeStoreMetaValue(value) {
6810
+ if (typeof value !== "string" || !value) {
6811
+ return void 0;
6812
+ }
6813
+ try {
6814
+ const asJson = parseJsonObject(value);
6815
+ if (asJson) {
6816
+ return asJson;
6817
+ }
6818
+ return parseJsonObject(Buffer.from(value, "hex").toString("utf8"));
6819
+ } catch {
6820
+ return void 0;
6821
+ }
6822
+ }
6823
+ async function resolveTranscriptContext(filePath, sessionId) {
6824
+ const sibling = await readJsonIfExists(path13.join(path13.dirname(filePath), "meta.json"));
6825
+ if (isPlainObject(sibling)) {
6826
+ const cwd = stringField(sibling, "cwd");
6827
+ return {
6828
+ cwd,
6829
+ project: cwd ? path13.basename(cwd) : void 0,
6830
+ startedAt: timestampFrom(sibling.createdAtMs),
6831
+ endedAt: timestampFrom(sibling.updatedAtMs)
6832
+ };
6833
+ }
6834
+ const cursorHomePath = cursorHomeFromTranscript(filePath);
6835
+ if (!cursorHomePath) {
6836
+ return {};
6837
+ }
6838
+ const index = await indexCursorChatMeta(cursorHomePath);
6839
+ return index.get(sessionId) || {};
6840
+ }
6841
+ async function parseCursorTranscriptFile(filePath, options) {
6842
+ const text = await readFile8(filePath, "utf8").catch(() => "");
6843
+ if (!text) {
6844
+ return [];
6845
+ }
6846
+ const lines = text.split("\n").filter(Boolean);
6847
+ const parsed = lines.map(parseJsonLine);
6848
+ const hasConversation = parsed.some((raw) => raw && (raw.role === "user" || raw.role === "assistant"));
6849
+ if (!hasConversation) {
6850
+ return [];
6851
+ }
6852
+ const sessionId = path13.basename(filePath, ".jsonl");
6853
+ const context = await resolveTranscriptContext(filePath, sessionId);
6854
+ const fileInfo = await stat7(filePath).catch(() => null);
6855
+ let cwd = context.cwd;
6856
+ let project = context.project;
6857
+ let model = context.model;
6858
+ let lastTs = context.startedAt || (fileInfo ? fileInfo.mtime.toISOString() : void 0);
6859
+ const fileMtime = fileInfo ? fileInfo.mtime.toISOString() : void 0;
6860
+ const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
6861
+ const sourcePathHash = `sha256:${createStableHash(filePath)}`;
6862
+ const events = [];
6863
+ let lineNumber = 0;
6864
+ let currentTurnId;
6865
+ let currentTurnStartedAt;
6866
+ let turnIndex = 0;
6867
+ let sessionStarted = false;
6868
+ let pendingTurnSuccess = true;
6869
+ const pendingTools = [];
6870
+ const push = (partial, topType) => {
6871
+ lineNumber += 1;
6872
+ const event = {
6873
+ schemaVersion: AGENT_TIME_SCHEMA_VERSION,
6874
+ source: SOURCE_ID,
6875
+ agent: AGENT_NAME,
6876
+ workspaceId,
6877
+ project,
6878
+ cwd,
6879
+ model,
6880
+ sessionId,
6881
+ ...partial
6882
+ };
6883
+ events.push(withBackfillRefs(event, {
6884
+ filePath,
6885
+ sourcePathHash,
6886
+ lineNumber,
6887
+ topType,
6888
+ payloadType: event.type,
6889
+ options
6890
+ }));
6891
+ };
6892
+ const ensureSessionStarted = (ts) => {
6893
+ if (sessionStarted) {
6894
+ return;
6895
+ }
6896
+ sessionStarted = true;
6897
+ push({
6898
+ ts,
6899
+ type: "session.started",
6900
+ confidence: "partial",
6901
+ refs: stringRefs({ sourceId: `${sessionId}:started` })
6902
+ }, "transcript");
6903
+ };
6904
+ const closeTurn = (endTs, success = pendingTurnSuccess) => {
6905
+ if (!currentTurnId || !currentTurnStartedAt) {
6906
+ pendingTools.length = 0;
6907
+ pendingTurnSuccess = true;
6908
+ return;
6909
+ }
6910
+ const turnId = currentTurnId;
6911
+ const startedAt = currentTurnStartedAt;
6912
+ const finishedAt = latestTimestamp(startedAt, endTs) || startedAt;
6913
+ const toolCount = pendingTools.length;
6914
+ for (const [index, item] of pendingTools.entries()) {
6915
+ const ts = timestampBetween(startedAt, finishedAt, index, toolCount);
6916
+ const fileActivities = fileActivitiesFromCursorTool(item.tool, item.input, ts, cwd);
6917
+ push({
6918
+ ts,
6919
+ type: "tool.started",
6920
+ turnId,
6921
+ tool: item.tool,
6922
+ operation: `${item.tool} started`,
6923
+ model,
6924
+ confidence: "exact",
6925
+ fileActivities: fileActivities.length > 0 ? fileActivities : void 0,
6926
+ refs: stringRefs({ sourceId: `${item.toolCallId}:started` })
6927
+ }, "tool");
6928
+ push({
6929
+ ts,
6930
+ type: "tool.completed",
6931
+ turnId,
6932
+ tool: item.tool,
6933
+ operation: `${item.tool} completed`,
6934
+ success,
6935
+ model,
6936
+ confidence: "exact",
6937
+ metrics: { toolCalls: 1 },
6938
+ refs: stringRefs({ sourceId: `${item.toolCallId}:completed` })
6939
+ }, "tool");
6940
+ if (item.command) {
6941
+ push({
6942
+ ts,
6943
+ type: "command.started",
6944
+ turnId,
6945
+ tool: item.tool,
6946
+ operation: "command started",
6947
+ confidence: "derived",
6948
+ refs: stringRefs({
6949
+ sourceId: `${item.toolCallId}:command:started`,
6950
+ commandHash: `sha256:${createStableHash(item.command)}`
6951
+ })
6952
+ }, "tool");
6953
+ push({
6954
+ ts,
6955
+ type: "command.completed",
6956
+ turnId,
6957
+ tool: item.tool,
6958
+ operation: "command completed",
6959
+ success,
6960
+ confidence: "derived",
6961
+ metrics: { commandCalls: 1 },
6962
+ refs: stringRefs({ sourceId: `${item.toolCallId}:command:completed` })
6963
+ }, "tool");
6964
+ }
6965
+ }
6966
+ pendingTools.length = 0;
6967
+ push({
6968
+ ts: finishedAt,
6969
+ type: "turn.completed",
6970
+ turnId,
6971
+ model,
6972
+ success,
6973
+ confidence: "derived",
6974
+ metrics: { durationMs: durationMsBetween(startedAt, finishedAt) },
6975
+ refs: stringRefs({ sourceId: `${sessionId}:${turnId}:completed` })
6976
+ }, "transcript");
6977
+ currentTurnId = void 0;
6978
+ currentTurnStartedAt = void 0;
6979
+ pendingTurnSuccess = true;
6980
+ };
6981
+ for (const raw of parsed) {
6982
+ if (!raw) {
6983
+ continue;
6984
+ }
6985
+ const role = stringField(raw, "role");
6986
+ const topType = stringField(raw, "type");
6987
+ const message = objectField(raw, "message");
6988
+ const body = Object.keys(message).length > 0 ? message : raw;
6989
+ const rawText = messageText(body);
6990
+ if (!cwd) {
6991
+ const workspaceMatch = rawText.match(/Workspace Path:\s*(.+)/);
6992
+ const found = workspaceMatch?.[1]?.trim();
6993
+ if (found) {
6994
+ cwd = found;
6995
+ project = path13.basename(found);
6996
+ }
6997
+ }
6998
+ if (role === "user") {
6999
+ const userBits = parseCursorUserText(rawText);
7000
+ const ts = timestampFrom(raw.timestamp) || userBits.ts;
7001
+ if (!ts) {
7002
+ continue;
7003
+ }
7004
+ lastTs = latestTimestamp(lastTs, ts) || ts;
7005
+ ensureSessionStarted(ts);
7006
+ closeTurn(ts);
7007
+ turnIndex += 1;
7008
+ currentTurnId = `turn_${turnIndex}`;
7009
+ currentTurnStartedAt = ts;
7010
+ pendingTurnSuccess = true;
7011
+ push({
7012
+ ts,
7013
+ type: "turn.started",
7014
+ turnId: currentTurnId,
7015
+ model,
7016
+ confidence: "partial",
7017
+ refs: stringRefs({ sourceId: `${sessionId}:${currentTurnId}:started` })
7018
+ }, "transcript");
7019
+ if (userBits.prompt) {
7020
+ push({
7021
+ ts,
7022
+ type: "prompt.submitted",
7023
+ turnId: currentTurnId,
7024
+ model,
7025
+ confidence: "partial",
7026
+ metrics: { prompts: 1, promptChars: userBits.prompt.length },
7027
+ refs: stringRefs({
7028
+ sourceId: `${sessionId}:${currentTurnId}:prompt`,
7029
+ promptHash: `sha256:${createStableHash(userBits.prompt)}`
7030
+ })
7031
+ }, "transcript");
7032
+ }
7033
+ continue;
7034
+ }
7035
+ if (role === "assistant") {
7036
+ if (!currentTurnId && lastTs) {
7037
+ ensureSessionStarted(lastTs);
7038
+ turnIndex += 1;
7039
+ currentTurnId = `turn_${turnIndex}`;
7040
+ currentTurnStartedAt = lastTs;
7041
+ }
7042
+ if (!currentTurnId) {
7043
+ continue;
7044
+ }
7045
+ for (const [index, toolUse] of messageToolUses(body).entries()) {
7046
+ const tool = toolNameOf(toolUse.name);
7047
+ const command = isShellTool(tool) ? stringField(toolUse.input, "command") || stringField(toolUse.input, "cmd") : void 0;
7048
+ pendingTools.push({
7049
+ tool,
7050
+ toolCallId: cleanId(
7051
+ toolUse.id || `${sessionId}:${currentTurnId}:${pendingTools.length}:${index}`
7052
+ ),
7053
+ input: toolUse.input,
7054
+ command
7055
+ });
7056
+ }
7057
+ continue;
7058
+ }
7059
+ if (topType === "turn_ended") {
7060
+ const status = (stringField(raw, "status") || "success").toLowerCase();
7061
+ pendingTurnSuccess = status !== "error" && status !== "failed" && status !== "cancelled";
7062
+ }
7063
+ }
7064
+ const endedAt = latestTimestamp(lastTs, context.endedAt, fileMtime);
7065
+ if (endedAt) {
7066
+ closeTurn(endedAt);
7067
+ if (sessionStarted) {
7068
+ push({
7069
+ ts: endedAt,
7070
+ type: "session.ended",
7071
+ confidence: "derived",
7072
+ refs: stringRefs({ sourceId: `${sessionId}:ended` })
7073
+ }, "transcript");
7074
+ }
7075
+ }
7076
+ return events;
7077
+ }
7078
+ async function listCursorComposerIds(dbPath) {
7079
+ const ids = /* @__PURE__ */ new Set();
7080
+ const info = await stat7(dbPath).catch(() => null);
7081
+ if (!info) {
7082
+ return ids;
7083
+ }
7084
+ try {
7085
+ const opened = await openCursorDb(dbPath);
7086
+ try {
7087
+ for (const composer of listComposers(opened.db)) {
7088
+ ids.add(composer.composerId);
7089
+ }
7090
+ } finally {
7091
+ opened.db.close();
7092
+ await opened.cleanup();
7093
+ }
7094
+ } catch {
7095
+ return ids;
7096
+ }
7097
+ return ids;
7098
+ }
7099
+ async function collectCursorTranscriptFiles(root, skipSessionIds) {
7100
+ const files = [];
7101
+ const seen = /* @__PURE__ */ new Set();
7102
+ for (const filePath of await listJsonlFiles(root)) {
7103
+ if (!isCursorTranscriptPath(filePath) && path13.basename(path13.dirname(filePath)) !== "agent-transcripts") {
7104
+ continue;
7105
+ }
7106
+ const sessionId = path13.basename(filePath, ".jsonl");
7107
+ if (!sessionId || seen.has(sessionId) || skipSessionIds?.has(sessionId)) {
7108
+ continue;
7109
+ }
7110
+ seen.add(sessionId);
7111
+ const info = await stat7(filePath).catch(() => null);
7112
+ if (!info) {
7113
+ continue;
7114
+ }
7115
+ files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
7116
+ }
7117
+ return files;
7118
+ }
7119
+ async function parseCursorSessionFile(filePath, options) {
7120
+ const base = path13.basename(filePath);
7121
+ if (base.endsWith(".jsonl")) {
7122
+ return parseCursorTranscriptFile(filePath, options);
7123
+ }
7124
+ if (base === "store.db") {
7125
+ return [];
7126
+ }
7127
+ if (base !== "state.vscdb" && !base.endsWith(".vscdb") && !base.endsWith(".db")) {
7128
+ return [];
7129
+ }
7130
+ const opened = await openCursorDb(filePath);
7131
+ const events = [];
7132
+ const sourcePathHash = `sha256:${createStableHash(filePath)}`;
7133
+ try {
7134
+ const composers = listComposers(opened.db);
7135
+ for (const composer of composers) {
7136
+ events.push(...parseComposer(opened.db, composer, filePath, sourcePathHash, options));
7137
+ }
7138
+ } finally {
7139
+ opened.db.close();
7140
+ await opened.cleanup();
7141
+ }
7142
+ return events;
7143
+ }
7144
+ function parseComposer(db, composer, filePath, sourcePathHash, options) {
7145
+ const sessionId = composer.composerId;
7146
+ const data = composer.data;
7147
+ const { cwd, project } = workspaceFromComposer(data);
7148
+ const model = modelFromComposer(data);
7149
+ const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
7150
+ const startedAt = timestampFrom(composer.createdAt) || timestampFrom(data.createdAt);
7151
+ const endedAt = timestampFrom(composer.lastUpdatedAt) || timestampFrom(data.lastUpdatedAt) || startedAt;
7152
+ if (!startedAt) {
7153
+ return [];
7154
+ }
7155
+ const bubbles = loadBubbles(db, sessionId);
7156
+ const events = [];
7157
+ let lineNumber = 0;
7158
+ let currentTurnId;
7159
+ let currentTurnStartedAt;
7160
+ let turnIndex = 0;
7161
+ let hasFileLineData = false;
7162
+ const push = (partial, topType) => {
7163
+ lineNumber += 1;
7164
+ const event = {
7165
+ schemaVersion: AGENT_TIME_SCHEMA_VERSION,
7166
+ source: SOURCE_ID,
7167
+ agent: AGENT_NAME,
7168
+ workspaceId,
7169
+ project,
7170
+ cwd,
7171
+ model,
7172
+ sessionId,
7173
+ ...partial
7174
+ };
7175
+ events.push(withBackfillRefs(event, {
7176
+ filePath,
7177
+ sourcePathHash,
7178
+ lineNumber,
7179
+ topType,
7180
+ payloadType: event.type,
7181
+ options
7182
+ }));
7183
+ };
7184
+ push({
7185
+ ts: startedAt,
7186
+ type: "session.started",
7187
+ confidence: "partial",
7188
+ refs: stringRefs({ sourceId: `${sessionId}:started` })
7189
+ }, "composer");
7190
+ for (const bubble of bubbles) {
7191
+ const ts = bubbleTs(bubble, startedAt);
7192
+ if (!ts) {
7193
+ continue;
7194
+ }
7195
+ const bubbleId = stringField(bubble, "bubbleId") || `bubble_${lineNumber}`;
7196
+ const bubbleType = numberField(bubble, "type");
7197
+ const capabilityType = numberField(bubble, "capabilityType");
7198
+ const toolFormer = objectField(bubble, "toolFormerData");
7199
+ const bubbleModel = stringField(objectField(bubble, "modelInfo"), "modelName") || model;
7200
+ if (bubbleType === BUBBLE_USER) {
7201
+ if (currentTurnId && currentTurnStartedAt) {
7202
+ push({
7203
+ ts,
7204
+ type: "turn.completed",
7205
+ turnId: currentTurnId,
7206
+ model: bubbleModel,
7207
+ success: true,
7208
+ confidence: "derived",
7209
+ metrics: { durationMs: durationMsBetween(currentTurnStartedAt, ts) },
7210
+ refs: stringRefs({ sourceId: `${sessionId}:${currentTurnId}:completed` })
7211
+ }, "bubble");
7212
+ }
7213
+ turnIndex += 1;
7214
+ currentTurnId = `turn_${turnIndex}`;
7215
+ currentTurnStartedAt = ts;
7216
+ const text = stringField(bubble, "text") || "";
7217
+ push({
7218
+ ts,
7219
+ type: "turn.started",
7220
+ turnId: currentTurnId,
7221
+ model: bubbleModel,
7222
+ confidence: "partial",
7223
+ refs: stringRefs({ sourceId: `${sessionId}:${currentTurnId}:started` })
7224
+ }, "bubble");
7225
+ if (text) {
7226
+ push({
7227
+ ts,
7228
+ type: "prompt.submitted",
7229
+ turnId: currentTurnId,
7230
+ model: bubbleModel,
7231
+ confidence: "partial",
7232
+ metrics: { prompts: 1, promptChars: text.length },
7233
+ refs: stringRefs({
7234
+ sourceId: `${sessionId}:${cleanId(bubbleId)}:prompt`,
7235
+ promptHash: `sha256:${createStableHash(text)}`
7236
+ })
7237
+ }, "bubble");
7238
+ }
7239
+ continue;
7240
+ }
7241
+ if (capabilityType === CAPABILITY_TOOL && Object.keys(toolFormer).length > 0) {
7242
+ const rawName = stringField(toolFormer, "name") || "unknown";
7243
+ const tool = toolNameOf(rawName);
7244
+ const toolCallId = cleanId(
7245
+ stringField(toolFormer, "toolCallId") || `${sessionId}:${cleanId(bubbleId)}`
7246
+ );
7247
+ const status = (stringField(toolFormer, "status") || "completed").toLowerCase();
7248
+ const success = status !== "error" && status !== "failed" && status !== "cancelled";
7249
+ const input = parseMaybeJsonObject(toolFormer.rawArgs);
7250
+ const params = parseMaybeJsonObject(toolFormer.params);
7251
+ const mergedInput = { ...params, ...input };
7252
+ const fileActivities = fileActivitiesFromCursorTool(tool, mergedInput, ts, cwd);
7253
+ if (fileActivities.some((file) => (file.linesAdded || 0) > 0 || (file.linesRemoved || 0) > 0)) {
7254
+ hasFileLineData = true;
7255
+ }
7256
+ push({
7257
+ ts,
7258
+ type: "tool.started",
7259
+ turnId: currentTurnId,
7260
+ tool,
7261
+ operation: `${tool} started`,
7262
+ model: bubbleModel,
7263
+ confidence: "exact",
7264
+ fileActivities: fileActivities.length > 0 ? fileActivities : void 0,
7265
+ refs: stringRefs({ sourceId: `${toolCallId}:started` })
7266
+ }, "tool");
7267
+ push({
7268
+ ts,
7269
+ type: success ? "tool.completed" : "tool.failed",
7270
+ turnId: currentTurnId,
7271
+ tool,
7272
+ operation: `${tool} completed`,
7273
+ success,
7274
+ model: bubbleModel,
7275
+ confidence: "exact",
7276
+ metrics: { toolCalls: 1 },
7277
+ refs: stringRefs({ sourceId: `${toolCallId}:completed` })
7278
+ }, "tool");
7279
+ if (isShellTool(tool)) {
7280
+ const command = stringField(mergedInput, "command") || stringField(mergedInput, "cmd");
7281
+ if (command) {
7282
+ push({
7283
+ ts,
7284
+ type: "command.started",
7285
+ turnId: currentTurnId,
7286
+ tool,
7287
+ operation: "command started",
7288
+ confidence: "derived",
7289
+ refs: stringRefs({
7290
+ sourceId: `${toolCallId}:command:started`,
7291
+ commandHash: `sha256:${createStableHash(command)}`
7292
+ })
7293
+ }, "tool");
7294
+ push({
7295
+ ts,
7296
+ type: success ? "command.completed" : "command.failed",
7297
+ turnId: currentTurnId,
7298
+ tool,
7299
+ operation: "command completed",
7300
+ success,
7301
+ confidence: "derived",
7302
+ metrics: { commandCalls: 1 },
7303
+ refs: stringRefs({ sourceId: `${toolCallId}:command:completed` })
7304
+ }, "tool");
7305
+ }
7306
+ }
7307
+ }
7308
+ const metrics = usageMetrics(objectField(bubble, "tokenCount"));
7309
+ if (metrics) {
7310
+ push({
7311
+ ts,
7312
+ type: "model.usage",
7313
+ turnId: currentTurnId,
7314
+ model: bubbleModel,
7315
+ confidence: "partial",
7316
+ metrics,
7317
+ refs: stringRefs({ sourceId: `${sessionId}:${cleanId(bubbleId)}:usage` })
7318
+ }, "bubble");
7319
+ }
7320
+ }
7321
+ if (currentTurnId && endedAt) {
7322
+ push({
7323
+ ts: endedAt,
7324
+ type: "turn.completed",
7325
+ turnId: currentTurnId,
7326
+ success: true,
7327
+ confidence: "derived",
7328
+ metrics: { durationMs: durationMsBetween(currentTurnStartedAt, endedAt) },
7329
+ refs: stringRefs({ sourceId: `${sessionId}:${currentTurnId}:completed` })
7330
+ }, "bubble");
7331
+ }
7332
+ const linesAdded = numberField(data, "totalLinesAdded");
7333
+ const linesRemoved = numberField(data, "totalLinesRemoved");
7334
+ if (!hasFileLineData && endedAt && ((linesAdded || 0) > 0 || (linesRemoved || 0) > 0)) {
7335
+ push({
7336
+ ts: endedAt,
7337
+ type: "file.changed",
7338
+ confidence: "partial",
7339
+ metrics: {
7340
+ linesAdded: linesAdded || void 0,
7341
+ linesRemoved: linesRemoved || void 0
7342
+ },
7343
+ refs: stringRefs({ sourceId: `${sessionId}:lines` })
7344
+ }, "composer");
7345
+ }
7346
+ if (endedAt) {
7347
+ push({
7348
+ ts: endedAt,
7349
+ type: "session.ended",
7350
+ confidence: "derived",
7351
+ refs: stringRefs({ sourceId: `${sessionId}:ended` })
7352
+ }, "composer");
7353
+ }
7354
+ return events;
7355
+ }
7356
+ async function cursorBackfillFiles(sourceRoot, home = os6.homedir(), env) {
7357
+ if (sourceRoot) {
7358
+ const info = await stat7(sourceRoot).catch(() => null);
7359
+ if (!info) {
7360
+ return [];
7361
+ }
7362
+ if (info.isDirectory()) {
7363
+ const files2 = [];
7364
+ for (const dbPath of await listFilesByExtensions(sourceRoot, [".vscdb", ".db"])) {
7365
+ const base = path13.basename(dbPath);
7366
+ if (base === "store.db" || base !== "state.vscdb" && !base.endsWith(".vscdb")) {
7367
+ continue;
7368
+ }
7369
+ const dbInfo = await stat7(dbPath).catch(() => null);
7370
+ if (dbInfo) {
7371
+ files2.push({ path: dbPath, modifiedAt: dbInfo.mtime.toISOString() });
7372
+ }
7373
+ }
7374
+ const skipIds2 = /* @__PURE__ */ new Set();
7375
+ for (const file of files2) {
7376
+ for (const id of await listCursorComposerIds(file.path)) {
7377
+ skipIds2.add(id);
7378
+ }
7379
+ }
7380
+ files2.push(...await collectCursorTranscriptFiles(sourceRoot, skipIds2));
7381
+ return files2;
7382
+ }
7383
+ if (path13.basename(sourceRoot) === "store.db") {
7384
+ return [];
7385
+ }
7386
+ return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
7387
+ }
7388
+ const files = [];
7389
+ const skipIds = /* @__PURE__ */ new Set();
7390
+ for (const candidatePath of cursorStateDbCandidates(home, env)) {
7391
+ const info = await stat7(candidatePath).catch(() => null);
7392
+ if (!info) {
6182
7393
  continue;
6183
7394
  }
6184
- const eventsPath = path12.join(sessionDir, entry, "events.jsonl");
6185
- const info = await stat6(eventsPath).catch(() => null);
6186
- if (info) {
6187
- results.push({ path: eventsPath, modifiedAt: info.mtime.toISOString() });
7395
+ files.push({ path: candidatePath, modifiedAt: info.mtime.toISOString() });
7396
+ for (const id of await listCursorComposerIds(candidatePath)) {
7397
+ skipIds.add(id);
6188
7398
  }
7399
+ break;
6189
7400
  }
6190
- return results;
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
- `;
7401
+ files.push(...await collectCursorTranscriptFiles(cursorProjectsDir(home, env), skipIds));
7402
+ return files;
6198
7403
  }
6199
- function copilotHome(home, env) {
6200
- const override = env?.COPILOT_HOME;
6201
- if (override && override.trim()) {
6202
- return path12.resolve(override);
7404
+ function cursorHookConfig() {
7405
+ const entry = { command: HOOK_COMMAND, timeout: HOOK_TIMEOUT_SECONDS };
7406
+ const hooks = {};
7407
+ for (const event of CURSOR_HOOK_EVENTS) {
7408
+ hooks[event] = [entry];
6203
7409
  }
6204
- return path12.join(home, ".copilot");
7410
+ return { version: 1, hooks };
6205
7411
  }
6206
- function createCopilotAdapter() {
7412
+ function createCursorAdapter() {
6207
7413
  return {
6208
- id: "copilot",
6209
- label: "GitHub Copilot",
6210
- agentName: "copilot",
6211
- kind: "agent",
7414
+ id: SOURCE_ID,
7415
+ label: "Cursor",
7416
+ agentName: AGENT_NAME,
7417
+ kind: "ide",
6212
7418
  detectPath(home, env) {
6213
- return copilotHome(home, env);
7419
+ return cursorHome(home, env);
6214
7420
  },
6215
7421
  installedPath(home, env) {
6216
- return path12.join(copilotHome(home, env), ".vibetime");
7422
+ return cursorHooksPath(home, env);
6217
7423
  },
6218
7424
  async isInstalled(home, env) {
6219
- try {
6220
- const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
6221
- return await pathExists2(path12.join(copilotHome(home, env), ".vibetime"));
6222
- } catch {
6223
- return false;
6224
- }
7425
+ const config = await readJsonIfExists(cursorHooksPath(home, env));
7426
+ return hasCursorHookCommand(config, HOOK_COMMAND);
6225
7427
  },
6226
7428
  installEntries(home, env) {
6227
7429
  return [{
6228
- kind: "file",
6229
- path: path12.join(copilotHome(home, env), ".vibetime"),
6230
- content: copilotPluginContent()
7430
+ kind: "cursor-hooks-json",
7431
+ path: cursorHooksPath(home, env),
7432
+ content: cursorHookConfig()
6231
7433
  }];
6232
7434
  },
6233
7435
  sourcePaths(home, env) {
6234
- return [path12.join(copilotHome(home, env), "session-state")];
7436
+ return [
7437
+ ...cursorStateDbCandidates(home, env),
7438
+ cursorProjectsDir(home, env),
7439
+ cursorChatsDir(home, env)
7440
+ ];
6235
7441
  },
6236
- parseSessionFile: parseCopilotSessionFile
7442
+ parseSessionFile: parseCursorSessionFile
6237
7443
  };
6238
7444
  }
6239
7445
 
6240
7446
  // src/adapters/grok-build.ts
6241
- import { readdir as readdir6, readFile as readFile8, stat as stat7 } from "node:fs/promises";
6242
- import path13 from "node:path";
7447
+ import { readdir as readdir7, readFile as readFile9, stat as stat8 } from "node:fs/promises";
7448
+ import path14 from "node:path";
6243
7449
  init_fs();
6244
7450
  var GROK_COST_TICKS_PER_USD = 1e9;
6245
- var SOURCE_ID = "grok-build";
6246
- var AGENT_NAME = "grok-build";
6247
- var HOOK_COMMAND = `vibetime hook --agent ${SOURCE_ID}`;
7451
+ var SOURCE_ID2 = "grok-build";
7452
+ var AGENT_NAME2 = "grok-build";
7453
+ var HOOK_COMMAND2 = `vibetime hook --agent ${SOURCE_ID2}`;
6248
7454
  function grokHome(home, env) {
6249
7455
  const override = env?.GROK_HOME;
6250
7456
  if (override && override.trim()) {
6251
- return path13.resolve(override);
7457
+ return path14.resolve(override);
6252
7458
  }
6253
- return path13.join(home, ".grok");
7459
+ return path14.join(home, ".grok");
6254
7460
  }
6255
7461
  function grokHooksPath(home, env) {
6256
- return path13.join(grokHome(home, env), "hooks", "vibetime.json");
7462
+ return path14.join(grokHome(home, env), "hooks", "vibetime.json");
6257
7463
  }
6258
7464
  function grokSessionsDir(home, env) {
6259
- return path13.join(grokHome(home, env), "sessions");
7465
+ return path14.join(grokHome(home, env), "sessions");
6260
7466
  }
6261
7467
  async function grokBackfillFiles(sourceRoot, home, env) {
6262
7468
  const root = sourceRoot || grokSessionsDir(home, env);
@@ -6264,12 +7470,12 @@ async function grokBackfillFiles(sourceRoot, home, env) {
6264
7470
  async function walk(dir) {
6265
7471
  let entries;
6266
7472
  try {
6267
- entries = await readdir6(dir, { withFileTypes: true });
7473
+ entries = await readdir7(dir, { withFileTypes: true });
6268
7474
  } catch {
6269
7475
  return;
6270
7476
  }
6271
7477
  for (const entry of entries) {
6272
- const entryPath = path13.join(dir, entry.name);
7478
+ const entryPath = path14.join(dir, entry.name);
6273
7479
  if (entry.isDirectory()) {
6274
7480
  await walk(entryPath);
6275
7481
  continue;
@@ -6278,7 +7484,7 @@ async function grokBackfillFiles(sourceRoot, home, env) {
6278
7484
  continue;
6279
7485
  }
6280
7486
  try {
6281
- const info = await stat7(entryPath);
7487
+ const info = await stat8(entryPath);
6282
7488
  files.push({ path: entryPath, modifiedAt: info.mtime.toISOString() });
6283
7489
  } catch {
6284
7490
  }
@@ -6292,18 +7498,18 @@ async function parseGrokSessionFile(filePath, options) {
6292
7498
  if (!sessionDir) {
6293
7499
  return [];
6294
7500
  }
6295
- const summaryPath = path13.join(sessionDir, "summary.json");
6296
- const eventsPath = path13.join(sessionDir, "events.jsonl");
6297
- const signalsPath = path13.join(sessionDir, "signals.json");
6298
- const updatesPath = path13.join(sessionDir, "updates.jsonl");
7501
+ const summaryPath = path14.join(sessionDir, "summary.json");
7502
+ const eventsPath = path14.join(sessionDir, "events.jsonl");
7503
+ const signalsPath = path14.join(sessionDir, "signals.json");
7504
+ const updatesPath = path14.join(sessionDir, "updates.jsonl");
6299
7505
  const summary = await readJsonIfExists(summaryPath);
6300
7506
  if (!isPlainObject(summary)) {
6301
7507
  return [];
6302
7508
  }
6303
7509
  const info = objectField(summary, "info");
6304
- const sessionId = stringField(info, "id") || path13.basename(sessionDir);
7510
+ const sessionId = stringField(info, "id") || path14.basename(sessionDir);
6305
7511
  const cwd = stringField(info, "cwd");
6306
- const project = cwd ? path13.basename(cwd) : void 0;
7512
+ const project = cwd ? path14.basename(cwd) : void 0;
6307
7513
  const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
6308
7514
  const model = stringField(summary, "current_model_id");
6309
7515
  const createdAt = timestampFrom(summary.created_at) || timestampFrom(summary.createdAt);
@@ -6330,8 +7536,8 @@ async function parseGrokSessionFile(filePath, options) {
6330
7536
  };
6331
7537
  const base = (partial) => ({
6332
7538
  schemaVersion: AGENT_TIME_SCHEMA_VERSION,
6333
- source: SOURCE_ID,
6334
- agent: AGENT_NAME,
7539
+ source: SOURCE_ID2,
7540
+ agent: AGENT_NAME2,
6335
7541
  workspaceId,
6336
7542
  project,
6337
7543
  cwd,
@@ -6351,7 +7557,7 @@ async function parseGrokSessionFile(filePath, options) {
6351
7557
  const pendingTools = /* @__PURE__ */ new Map();
6352
7558
  let eventsText = "";
6353
7559
  try {
6354
- eventsText = await readFile8(eventsPath, "utf8");
7560
+ eventsText = await readFile9(eventsPath, "utf8");
6355
7561
  } catch {
6356
7562
  }
6357
7563
  const lines = eventsText.split("\n").filter(Boolean);
@@ -6429,7 +7635,7 @@ async function parseGrokSessionFile(filePath, options) {
6429
7635
  fileActivities: fileActivities.length > 0 ? fileActivities : void 0,
6430
7636
  refs: stringRefs({ sourceId: `${toolId}:started` })
6431
7637
  }), lineNumber, type, "tool.started");
6432
- if (isShellTool(toolName)) {
7638
+ if (isShellTool2(toolName)) {
6433
7639
  const command = stringField(knownInput?.input || {}, "command");
6434
7640
  if (command) {
6435
7641
  push(base({
@@ -6470,7 +7676,7 @@ async function parseGrokSessionFile(filePath, options) {
6470
7676
  },
6471
7677
  refs: stringRefs({ sourceId: `${toolId}:completed` })
6472
7678
  }), lineNumber, type, "tool.completed");
6473
- if (isShellTool(pending?.name || toolName)) {
7679
+ if (isShellTool2(pending?.name || toolName)) {
6474
7680
  push(base({
6475
7681
  ts,
6476
7682
  type: success ? "command.completed" : "command.failed",
@@ -6527,7 +7733,7 @@ async function parseGrokSessionFile(filePath, options) {
6527
7733
  nextLine += 1;
6528
7734
  }
6529
7735
  }
6530
- const hunkPath = path13.join(sessionDir, "hunk_records.jsonl");
7736
+ const hunkPath = path14.join(sessionDir, "hunk_records.jsonl");
6531
7737
  const hunkActivities = await loadHunkFileActivities(hunkPath, cwd);
6532
7738
  if (hunkActivities.length > 0) {
6533
7739
  for (const event of events) {
@@ -6591,9 +7797,9 @@ async function parseGrokSessionFile(filePath, options) {
6591
7797
  return events;
6592
7798
  }
6593
7799
  function resolveSessionDir(filePath) {
6594
- const base = path13.basename(filePath);
7800
+ const base = path14.basename(filePath);
6595
7801
  if (base === "summary.json" || base === "events.jsonl" || base === "signals.json") {
6596
- return path13.dirname(filePath);
7802
+ return path14.dirname(filePath);
6597
7803
  }
6598
7804
  if (/^[0-9a-f]{8}-[0-9a-f-]{27,}$/i.test(base)) {
6599
7805
  return filePath;
@@ -6616,7 +7822,7 @@ function findPendingToolId(pending, toolName) {
6616
7822
  }
6617
7823
  return void 0;
6618
7824
  }
6619
- function isShellTool(name) {
7825
+ function isShellTool2(name) {
6620
7826
  if (!name) {
6621
7827
  return false;
6622
7828
  }
@@ -6625,7 +7831,7 @@ function isShellTool(name) {
6625
7831
  }
6626
7832
  function fileActivitiesFromTool(toolName, input, ts, cwd) {
6627
7833
  const lower = toolName.toLowerCase();
6628
- if (isShellTool(toolName)) {
7834
+ if (isShellTool2(toolName)) {
6629
7835
  const command = stringField(input, "command");
6630
7836
  if (command) {
6631
7837
  return fileActivitiesFromShellCommand(command, ts, cwd, cwd);
@@ -6679,17 +7885,17 @@ function fileActivitiesFromTool(toolName, input, ts, cwd) {
6679
7885
  return [];
6680
7886
  }
6681
7887
  function displayPath(filePath, cwd) {
6682
- if (!cwd || !path13.isAbsolute(filePath)) {
7888
+ if (!cwd || !path14.isAbsolute(filePath)) {
6683
7889
  return filePath;
6684
7890
  }
6685
- const relative = path13.relative(cwd, filePath);
6686
- return relative && !relative.startsWith("..") && !path13.isAbsolute(relative) ? relative : filePath;
7891
+ const relative = path14.relative(cwd, filePath);
7892
+ return relative && !relative.startsWith("..") && !path14.isAbsolute(relative) ? relative : filePath;
6687
7893
  }
6688
7894
  async function loadToolInputsFromUpdates(updatesPath) {
6689
7895
  const map = /* @__PURE__ */ new Map();
6690
7896
  let text = "";
6691
7897
  try {
6692
- text = await readFile8(updatesPath, "utf8");
7898
+ text = await readFile9(updatesPath, "utf8");
6693
7899
  } catch {
6694
7900
  return map;
6695
7901
  }
@@ -6760,7 +7966,7 @@ async function loadTurnUsagesFromUpdates(updatesPath) {
6760
7966
  const turns = [];
6761
7967
  let text = "";
6762
7968
  try {
6763
- text = await readFile8(updatesPath, "utf8");
7969
+ text = await readFile9(updatesPath, "utf8");
6764
7970
  } catch {
6765
7971
  return turns;
6766
7972
  }
@@ -6849,7 +8055,7 @@ async function loadHunkFileActivities(hunkPath, cwd) {
6849
8055
  const activities = [];
6850
8056
  let text = "";
6851
8057
  try {
6852
- text = await readFile8(hunkPath, "utf8");
8058
+ text = await readFile9(hunkPath, "utf8");
6853
8059
  } catch {
6854
8060
  return activities;
6855
8061
  }
@@ -6891,7 +8097,7 @@ async function loadHunkFileActivities(hunkPath, cwd) {
6891
8097
  }
6892
8098
  return activities;
6893
8099
  }
6894
- var grokHandler = (msg) => hookHandler(SOURCE_ID, msg);
8100
+ var grokHandler = (msg) => hookHandler(SOURCE_ID2, msg);
6895
8101
  function hookConfig5() {
6896
8102
  const anyTool = [{ matcher: ".*", hooks: [grokHandler("Reporting tool activity")] }];
6897
8103
  return {
@@ -6913,9 +8119,9 @@ function hookConfig5() {
6913
8119
  }
6914
8120
  function createGrokBuildAdapter() {
6915
8121
  return {
6916
- id: SOURCE_ID,
8122
+ id: SOURCE_ID2,
6917
8123
  label: "Grok Build",
6918
- agentName: AGENT_NAME,
8124
+ agentName: AGENT_NAME2,
6919
8125
  kind: "agent",
6920
8126
  detectPath(home, env) {
6921
8127
  return grokHome(home, env);
@@ -6924,7 +8130,7 @@ function createGrokBuildAdapter() {
6924
8130
  return grokHooksPath(home, env);
6925
8131
  },
6926
8132
  async isInstalled(home, env) {
6927
- return isHooksJsonInstalled(grokHooksPath(home, env), HOOK_COMMAND);
8133
+ return isHooksJsonInstalled(grokHooksPath(home, env), HOOK_COMMAND2);
6928
8134
  },
6929
8135
  installEntries(home, env) {
6930
8136
  return [{
@@ -6941,8 +8147,8 @@ function createGrokBuildAdapter() {
6941
8147
  }
6942
8148
 
6943
8149
  // src/adapters/kimi-code.ts
6944
- import { readFile as readFile9 } from "node:fs/promises";
6945
- import path14 from "node:path";
8150
+ import { readFile as readFile10 } from "node:fs/promises";
8151
+ import path15 from "node:path";
6946
8152
 
6947
8153
  // src/lib/toml-hooks.ts
6948
8154
  init_fs();
@@ -7089,20 +8295,20 @@ function tomlNumberField(body, key) {
7089
8295
  }
7090
8296
 
7091
8297
  // src/adapters/kimi-code.ts
7092
- var HOOK_COMMAND2 = "vibetime hook --agent kimi-code";
7093
- var HOOK_TIMEOUT_SECONDS = 10;
8298
+ var HOOK_COMMAND3 = "vibetime hook --agent kimi-code";
8299
+ var HOOK_TIMEOUT_SECONDS2 = 10;
7094
8300
  function kimiCodeHome(home, env) {
7095
8301
  const override = env?.KIMI_CODE_HOME || env?.KIMI_HOME;
7096
8302
  if (override && override.trim()) {
7097
- return path14.resolve(override);
8303
+ return path15.resolve(override);
7098
8304
  }
7099
- return path14.join(home, ".kimi-code");
8305
+ return path15.join(home, ".kimi-code");
7100
8306
  }
7101
8307
  function kimiCodeSessionsDir(home, env) {
7102
- return path14.join(kimiCodeHome(home, env), "sessions");
8308
+ return path15.join(kimiCodeHome(home, env), "sessions");
7103
8309
  }
7104
8310
  function kimiCodeConfigPath(home, env) {
7105
- return path14.join(kimiCodeHome(home, env), "config.toml");
8311
+ return path15.join(kimiCodeHome(home, env), "config.toml");
7106
8312
  }
7107
8313
  function kimiHookRules() {
7108
8314
  const events = [
@@ -7119,8 +8325,8 @@ function kimiHookRules() {
7119
8325
  ];
7120
8326
  return events.map((event) => ({
7121
8327
  event,
7122
- command: HOOK_COMMAND2,
7123
- timeout: HOOK_TIMEOUT_SECONDS
8328
+ command: HOOK_COMMAND3,
8329
+ timeout: HOOK_TIMEOUT_SECONDS2
7124
8330
  }));
7125
8331
  }
7126
8332
  function baseKimiEvent(event) {
@@ -7179,10 +8385,10 @@ function sessionIdFromWirePath(filePath) {
7179
8385
  return match?.[1];
7180
8386
  }
7181
8387
  async function readSessionState(filePath) {
7182
- const sessionDir = path14.dirname(path14.dirname(path14.dirname(filePath)));
7183
- const statePath = path14.join(sessionDir, "state.json");
8388
+ const sessionDir = path15.dirname(path15.dirname(path15.dirname(filePath)));
8389
+ const statePath = path15.join(sessionDir, "state.json");
7184
8390
  try {
7185
- const text = await readFile9(statePath, "utf8");
8391
+ const text = await readFile10(statePath, "utf8");
7186
8392
  const raw = JSON.parse(text);
7187
8393
  if (!isPlainObject(raw)) {
7188
8394
  return {};
@@ -7197,15 +8403,15 @@ async function readSessionState(filePath) {
7197
8403
  }
7198
8404
  }
7199
8405
  async function parseKimiCodeSessionFile(filePath, options) {
7200
- if (path14.basename(filePath) !== "wire.jsonl") {
8406
+ if (path15.basename(filePath) !== "wire.jsonl") {
7201
8407
  return [];
7202
8408
  }
7203
- const text = await readFile9(filePath, "utf8");
8409
+ const text = await readFile10(filePath, "utf8");
7204
8410
  const lines = text.split("\n").filter(Boolean);
7205
8411
  const stateMeta = await readSessionState(filePath);
7206
8412
  let sessionId = stateMeta.sessionId || sessionIdFromWirePath(filePath);
7207
8413
  let cwd = stateMeta.cwd;
7208
- let project = cwd ? path14.basename(cwd) : void 0;
8414
+ let project = cwd ? path15.basename(cwd) : void 0;
7209
8415
  let model;
7210
8416
  let provider;
7211
8417
  let reasoningEffort;
@@ -7251,7 +8457,7 @@ async function parseKimiCodeSessionFile(filePath, options) {
7251
8457
  const disclosedCwd = stringField(disclosure, "cwd");
7252
8458
  if (disclosedCwd) {
7253
8459
  cwd = disclosedCwd;
7254
- project = path14.basename(disclosedCwd);
8460
+ project = path15.basename(disclosedCwd);
7255
8461
  }
7256
8462
  continue;
7257
8463
  }
@@ -7565,7 +8771,7 @@ function createKimiCodeAdapter() {
7565
8771
  return kimiCodeConfigPath(home, env);
7566
8772
  },
7567
8773
  async isInstalled(home, env) {
7568
- return hasTomlHookCommand(kimiCodeConfigPath(home, env), HOOK_COMMAND2);
8774
+ return hasTomlHookCommand(kimiCodeConfigPath(home, env), HOOK_COMMAND3);
7569
8775
  },
7570
8776
  installEntries(home, env) {
7571
8777
  return [{
@@ -7582,8 +8788,8 @@ function createKimiCodeAdapter() {
7582
8788
  }
7583
8789
 
7584
8790
  // src/adapters/opencode.ts
7585
- import os6 from "node:os";
7586
- import path15 from "node:path";
8791
+ import os7 from "node:os";
8792
+ import path16 from "node:path";
7587
8793
  async function parseOpenCodeSessionFile(dbPath, options) {
7588
8794
  const { DatabaseSync } = await import("node:sqlite");
7589
8795
  if (!dbPath.endsWith(".db")) {
@@ -7642,7 +8848,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
7642
8848
  const isSubagent = sessionId !== rawSessionId;
7643
8849
  const sessionEventStart = events.length;
7644
8850
  const cwd = session.directory || session.path || void 0;
7645
- const project = cwd ? path15.basename(cwd) : void 0;
8851
+ const project = cwd ? path16.basename(cwd) : void 0;
7646
8852
  const sessionTs = msToIso(session.time_created);
7647
8853
  events.push(baseOpenCodeEvent({
7648
8854
  ts: sessionTs,
@@ -7750,7 +8956,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
7750
8956
  const provider = currentProvider;
7751
8957
  const pathObj = objectField(info, "path");
7752
8958
  const assistantCwd = stringField(pathObj, "cwd") || cwd;
7753
- const assistantProject = assistantCwd ? path15.basename(assistantCwd) : project;
8959
+ const assistantProject = assistantCwd ? path16.basename(assistantCwd) : project;
7754
8960
  const completedTs = numberField(objectField(info, "time"), "completed");
7755
8961
  const createdTs = timeCreated;
7756
8962
  const tokens = opencodeUsageFromInfo(info);
@@ -8024,33 +9230,33 @@ function opencodeUsageFromInfo(info) {
8024
9230
  function opencodeConfigDir(home, env) {
8025
9231
  const override = env?.OPENCODE_CONFIG_DIR;
8026
9232
  if (override && override.trim()) {
8027
- return path15.resolve(override);
9233
+ return path16.resolve(override);
8028
9234
  }
8029
9235
  const xdgConfig = env?.XDG_CONFIG_HOME;
8030
9236
  if (xdgConfig && xdgConfig.trim()) {
8031
- return path15.join(path15.resolve(xdgConfig), "opencode");
9237
+ return path16.join(path16.resolve(xdgConfig), "opencode");
8032
9238
  }
8033
- return path15.join(home, ".config", "opencode");
9239
+ return path16.join(home, ".config", "opencode");
8034
9240
  }
8035
9241
  function opencodeDataCandidates(home, env) {
8036
9242
  const xdgData = env?.XDG_DATA_HOME;
8037
- const primary = xdgData && xdgData.trim() ? path15.join(path15.resolve(xdgData), "opencode", "opencode.db") : path15.join(home, ".local", "share", "opencode", "opencode.db");
8038
- return [primary, path15.join(home, ".opencode", "opencode.db")];
9243
+ const primary = xdgData && xdgData.trim() ? path16.join(path16.resolve(xdgData), "opencode", "opencode.db") : path16.join(home, ".local", "share", "opencode", "opencode.db");
9244
+ return [primary, path16.join(home, ".opencode", "opencode.db")];
8039
9245
  }
8040
- async function opencodeBackfillFiles(sourceRoot, home = os6.homedir(), env) {
8041
- const { stat: stat15 } = await import("node:fs/promises");
9246
+ async function opencodeBackfillFiles(sourceRoot, home = os7.homedir(), env) {
9247
+ const { stat: stat16 } = await import("node:fs/promises");
8042
9248
  if (sourceRoot) {
8043
9249
  if (!sourceRoot.endsWith(".db")) {
8044
9250
  return [];
8045
9251
  }
8046
- const info = await stat15(sourceRoot).catch(() => null);
9252
+ const info = await stat16(sourceRoot).catch(() => null);
8047
9253
  if (!info) {
8048
9254
  return [];
8049
9255
  }
8050
9256
  return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
8051
9257
  }
8052
9258
  for (const candidatePath of opencodeDataCandidates(home, env)) {
8053
- const info = await stat15(candidatePath).catch(() => null);
9259
+ const info = await stat16(candidatePath).catch(() => null);
8054
9260
  if (info) {
8055
9261
  return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
8056
9262
  }
@@ -8125,12 +9331,12 @@ function createOpenCodeAdapter() {
8125
9331
  return opencodeConfigDir(home, env);
8126
9332
  },
8127
9333
  installedPath(home, env) {
8128
- return path15.join(opencodeConfigDir(home, env), PLUGIN_PATH);
9334
+ return path16.join(opencodeConfigDir(home, env), PLUGIN_PATH);
8129
9335
  },
8130
9336
  async isInstalled(home, env) {
8131
9337
  try {
8132
9338
  const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
8133
- return await pathExists2(path15.join(opencodeConfigDir(home, env), PLUGIN_PATH)) || await pathExists2(path15.join(".opencode", PLUGIN_PATH));
9339
+ return await pathExists2(path16.join(opencodeConfigDir(home, env), PLUGIN_PATH)) || await pathExists2(path16.join(".opencode", PLUGIN_PATH));
8134
9340
  } catch {
8135
9341
  return false;
8136
9342
  }
@@ -8138,7 +9344,7 @@ function createOpenCodeAdapter() {
8138
9344
  installEntries(home, env) {
8139
9345
  return [{
8140
9346
  kind: "file",
8141
- path: path15.join(opencodeConfigDir(home, env), PLUGIN_PATH),
9347
+ path: path16.join(opencodeConfigDir(home, env), PLUGIN_PATH),
8142
9348
  content: opencodePluginContent()
8143
9349
  }];
8144
9350
  },
@@ -8150,14 +9356,37 @@ function createOpenCodeAdapter() {
8150
9356
  }
8151
9357
 
8152
9358
  // src/adapters/pi.ts
8153
- import { readdir as readdir7, readFile as readFile10, stat as stat8 } from "node:fs/promises";
8154
- import os7 from "node:os";
8155
- import path16 from "node:path";
9359
+ import { readdir as readdir8, readFile as readFile11, stat as stat9 } from "node:fs/promises";
9360
+ import os8 from "node:os";
9361
+ import path17 from "node:path";
8156
9362
  init_fs();
9363
+ function piHostSessionIdFromBasename(basename) {
9364
+ const withoutExt = basename.endsWith(".jsonl") ? basename.slice(0, -".jsonl".length) : basename;
9365
+ if (!withoutExt || withoutExt === "session") {
9366
+ return void 0;
9367
+ }
9368
+ const underscore = withoutExt.indexOf("_");
9369
+ if (underscore <= 0 || underscore === withoutExt.length - 1) {
9370
+ return withoutExt;
9371
+ }
9372
+ return withoutExt.slice(underscore + 1);
9373
+ }
9374
+ function resolvePiBackfillGroupId(filePath) {
9375
+ const normalized = filePath.replaceAll("\\", "/");
9376
+ const base = path17.basename(filePath);
9377
+ const nest = normalized.match(/([^/]+)\/[^/]+\/run-\d+\/session\.jsonl$/);
9378
+ if (nest) {
9379
+ return piHostSessionIdFromBasename(nest[1]);
9380
+ }
9381
+ if (base.endsWith(".jsonl") && base !== "session.jsonl") {
9382
+ return piHostSessionIdFromBasename(base);
9383
+ }
9384
+ return void 0;
9385
+ }
8157
9386
  function parsePiSubagentLink(filePath, headerParentSession) {
8158
9387
  if (headerParentSession) {
8159
- const parentFile = path16.isAbsolute(headerParentSession) ? headerParentSession : void 0;
8160
- const parentSessionId2 = parentFile ? path16.basename(parentFile, ".jsonl") : headerParentSession;
9388
+ const parentFile = path17.isAbsolute(headerParentSession) ? headerParentSession : void 0;
9389
+ const parentSessionId2 = parentFile ? path17.basename(parentFile, ".jsonl") : headerParentSession;
8161
9390
  return { parentSessionId: parentSessionId2, parentSessionFile: parentFile, explicit: true };
8162
9391
  }
8163
9392
  const normalized = filePath.replaceAll("\\", "/");
@@ -8170,20 +9399,20 @@ function parsePiSubagentLink(filePath, headerParentSession) {
8170
9399
  return void 0;
8171
9400
  }
8172
9401
  const parentSessionId = parentBasename.endsWith(".jsonl") ? parentBasename.slice(0, -".jsonl".length) : parentBasename;
8173
- const parentDir = path16.dirname(path16.dirname(path16.dirname(filePath)));
8174
- const parentSessionFile = path16.join(path16.dirname(parentDir), `${parentBasename}.jsonl`);
9402
+ const parentDir = path17.dirname(path17.dirname(path17.dirname(filePath)));
9403
+ const parentSessionFile = path17.join(path17.dirname(parentDir), `${parentBasename}.jsonl`);
8175
9404
  return { parentSessionId, parentSessionFile, explicit: false };
8176
9405
  }
8177
9406
  async function resolveParentContext(link, options) {
8178
9407
  if (link.parentSessionFile) {
8179
9408
  try {
8180
- const text = await readFile10(link.parentSessionFile, "utf8");
9409
+ const text = await readFile11(link.parentSessionFile, "utf8");
8181
9410
  const firstLine = text.split("\n").find((line) => line.trim().length > 0);
8182
9411
  const raw = firstLine ? parseJsonLine(firstLine) : void 0;
8183
9412
  if (raw) {
8184
9413
  const id = stringField(raw, "id");
8185
9414
  const cwd = stringField(raw, "cwd");
8186
- const project = cwd ? path16.basename(cwd) : void 0;
9415
+ const project = cwd ? path17.basename(cwd) : void 0;
8187
9416
  if (id || cwd || project) {
8188
9417
  return { sessionId: id, cwd, project };
8189
9418
  }
@@ -8232,7 +9461,7 @@ function rebuildEventIdentity2(event) {
8232
9461
  };
8233
9462
  }
8234
9463
  async function parsePiSessionFile(filePath, options) {
8235
- const text = await readFile10(filePath, "utf8");
9464
+ const text = await readFile11(filePath, "utf8");
8236
9465
  const lines = text.split("\n").filter(Boolean);
8237
9466
  let sessionId;
8238
9467
  let cwd;
@@ -8264,7 +9493,7 @@ async function parsePiSessionFile(filePath, options) {
8264
9493
  sessionId = stringField(raw, "id") || state.sessionId;
8265
9494
  state.sessionId = sessionId || state.sessionId;
8266
9495
  cwd = stringField(raw, "cwd") || cwd;
8267
- project = cwd ? path16.basename(cwd) : project;
9496
+ project = cwd ? path17.basename(cwd) : project;
8268
9497
  headerParentSession = stringField(raw, "parentSession") || headerParentSession;
8269
9498
  continue;
8270
9499
  }
@@ -8471,7 +9700,7 @@ async function parsePiFile(filePath, options) {
8471
9700
  async function parsePiWorkflowRunFile(filePath, options) {
8472
9701
  let raw;
8473
9702
  try {
8474
- raw = JSON.parse(await readFile10(filePath, "utf8"));
9703
+ raw = JSON.parse(await readFile11(filePath, "utf8"));
8475
9704
  } catch {
8476
9705
  return [];
8477
9706
  }
@@ -8539,17 +9768,17 @@ async function parsePiWorkflowRunFile(filePath, options) {
8539
9768
  return state.events.filter((event) => matchesBackfillFilters(event, options));
8540
9769
  }
8541
9770
  async function findPiSessionFileById(sessionId, options) {
8542
- const sessionsDir = piSessionDir(path16.resolve(stringOption(options.home) || os7.homedir()));
9771
+ const sessionsDir = piSessionDir(path17.resolve(stringOption(options.home) || os8.homedir()));
8543
9772
  try {
8544
- const projectDirs = await readdir7(sessionsDir, { withFileTypes: true });
9773
+ const projectDirs = await readdir8(sessionsDir, { withFileTypes: true });
8545
9774
  for (const dir of projectDirs) {
8546
9775
  if (!dir.isDirectory()) {
8547
9776
  continue;
8548
9777
  }
8549
- const names = await readdir7(path16.join(sessionsDir, dir.name));
9778
+ const names = await readdir8(path17.join(sessionsDir, dir.name));
8550
9779
  const hit = names.find((name) => name.endsWith(`_${sessionId}.jsonl`));
8551
9780
  if (hit) {
8552
- return path16.join(sessionsDir, dir.name, hit);
9781
+ return path17.join(sessionsDir, dir.name, hit);
8553
9782
  }
8554
9783
  }
8555
9784
  } catch {
@@ -8791,29 +10020,48 @@ export default function (pi: ExtensionAPI) {
8791
10020
  function piAgentDir(home, env) {
8792
10021
  const override = env?.PI_CODING_AGENT_DIR;
8793
10022
  if (override && override.trim()) {
8794
- return path16.resolve(override);
10023
+ return path17.resolve(override);
8795
10024
  }
8796
- return path16.join(home, ".pi", "agent");
10025
+ return path17.join(home, ".pi", "agent");
8797
10026
  }
8798
10027
  function piSessionDir(home, env) {
8799
10028
  const override = env?.PI_CODING_AGENT_SESSION_DIR;
8800
10029
  if (override && override.trim()) {
8801
- return path16.resolve(override);
10030
+ return path17.resolve(override);
8802
10031
  }
8803
- return path16.join(piAgentDir(home, env), "sessions");
10032
+ return path17.join(piAgentDir(home, env), "sessions");
8804
10033
  }
8805
10034
  function piWorkflowProjectsDir(home) {
8806
- return path16.join(home, ".pi", "workflows", "projects");
10035
+ return path17.join(home, ".pi", "workflows", "projects");
10036
+ }
10037
+ async function workflowRunGroupId(filePath) {
10038
+ try {
10039
+ const raw = JSON.parse(await readFile11(filePath, "utf8"));
10040
+ if (!isPlainObject(raw)) {
10041
+ return void 0;
10042
+ }
10043
+ return stringField(raw, "sessionId");
10044
+ } catch {
10045
+ return void 0;
10046
+ }
8807
10047
  }
8808
- async function piBackfillFiles(sourceRoot, home = os7.homedir(), env) {
10048
+ async function piBackfillFiles(sourceRoot, home = os8.homedir(), env) {
8809
10049
  const lists = sourceRoot ? [await listFilesByExtensions(sourceRoot, [".jsonl", ".json"])] : await Promise.all([
8810
10050
  listFilesByExtensions(piSessionDir(home, env), [".jsonl"]),
8811
10051
  listFilesByExtensions(piWorkflowProjectsDir(home), [".json"])
8812
10052
  ]);
8813
10053
  const files = lists.flat().sort();
8814
10054
  return Promise.all(files.map(async (filePath) => {
8815
- const info = await stat8(filePath);
8816
- return { path: filePath, modifiedAt: info.mtime.toISOString() };
10055
+ const info = await stat9(filePath);
10056
+ let groupId = resolvePiBackfillGroupId(filePath);
10057
+ if (!groupId && filePath.endsWith(".json")) {
10058
+ groupId = await workflowRunGroupId(filePath);
10059
+ }
10060
+ return {
10061
+ path: filePath,
10062
+ modifiedAt: info.mtime.toISOString(),
10063
+ ...groupId ? { groupId } : {}
10064
+ };
8817
10065
  }));
8818
10066
  }
8819
10067
  function createPiAdapter() {
@@ -8826,12 +10074,12 @@ function createPiAdapter() {
8826
10074
  return piAgentDir(home, env);
8827
10075
  },
8828
10076
  installedPath(home, env) {
8829
- return path16.join(piAgentDir(home, env), "extensions", "vibetime.ts");
10077
+ return path17.join(piAgentDir(home, env), "extensions", "vibetime.ts");
8830
10078
  },
8831
10079
  async isInstalled(home, env) {
8832
10080
  try {
8833
10081
  const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
8834
- return await pathExists2(path16.join(piAgentDir(home, env), "extensions", "vibetime.ts"));
10082
+ return await pathExists2(path17.join(piAgentDir(home, env), "extensions", "vibetime.ts"));
8835
10083
  } catch {
8836
10084
  return false;
8837
10085
  }
@@ -8839,7 +10087,7 @@ function createPiAdapter() {
8839
10087
  installEntries(home, env) {
8840
10088
  return [{
8841
10089
  kind: "file",
8842
- path: path16.join(piAgentDir(home, env), "extensions", "vibetime.ts"),
10090
+ path: path17.join(piAgentDir(home, env), "extensions", "vibetime.ts"),
8843
10091
  content: piExtensionContent()
8844
10092
  }];
8845
10093
  },
@@ -8851,14 +10099,14 @@ function createPiAdapter() {
8851
10099
  }
8852
10100
 
8853
10101
  // src/adapters/qoder-cn.ts
8854
- import { readdir as readdir8, readFile as readFile11, stat as stat9 } from "node:fs/promises";
8855
- import os9 from "node:os";
8856
- import path18 from "node:path";
10102
+ import { readdir as readdir9, readFile as readFile12, stat as stat10 } from "node:fs/promises";
10103
+ import os10 from "node:os";
10104
+ import path19 from "node:path";
8857
10105
 
8858
10106
  // src/adapters/qoder-local-db.ts
8859
10107
  import { access } from "node:fs/promises";
8860
- import os8 from "node:os";
8861
- import path17 from "node:path";
10108
+ import os9 from "node:os";
10109
+ import path18 from "node:path";
8862
10110
  function takeQoderDbModelCall(calls, requestId, blockStart) {
8863
10111
  if (requestId) {
8864
10112
  const call = calls.byRequestId.get(requestId)?.shift();
@@ -8875,14 +10123,14 @@ function takeQoderDbModelCall(calls, requestId, blockStart) {
8875
10123
  }
8876
10124
  return calls.ordered.shift();
8877
10125
  }
8878
- function appDataRoot(appDirName, home = os8.homedir()) {
10126
+ function appDataRoot(appDirName, home = os9.homedir()) {
8879
10127
  if (process.platform === "darwin") {
8880
- return path17.join(home, "Library", "Application Support", appDirName);
10128
+ return path18.join(home, "Library", "Application Support", appDirName);
8881
10129
  }
8882
10130
  if (process.platform === "win32") {
8883
- return path17.join(process.env.APPDATA || path17.join(home, "AppData", "Roaming"), appDirName);
10131
+ return path18.join(process.env.APPDATA || path18.join(home, "AppData", "Roaming"), appDirName);
8884
10132
  }
8885
- return path17.join(home, ".config", appDirName);
10133
+ return path18.join(home, ".config", appDirName);
8886
10134
  }
8887
10135
  function qoderLocalDbCandidates(appDirName) {
8888
10136
  const candidates = [];
@@ -8892,8 +10140,8 @@ function qoderLocalDbCandidates(appDirName) {
8892
10140
  }
8893
10141
  const configRoot = appDataRoot(appDirName);
8894
10142
  candidates.push(
8895
- path17.join(configRoot, "SharedClientCache", "cache", "db", "local.db"),
8896
- path17.join(configRoot, "SharedClientCache", "db", "local.db")
10143
+ path18.join(configRoot, "SharedClientCache", "cache", "db", "local.db"),
10144
+ path18.join(configRoot, "SharedClientCache", "db", "local.db")
8897
10145
  );
8898
10146
  return candidates;
8899
10147
  }
@@ -8902,7 +10150,7 @@ async function loadQoderIdeModelCatalog(appDirName, home) {
8902
10150
  try {
8903
10151
  const { DatabaseSync } = await import("node:sqlite");
8904
10152
  const db = new DatabaseSync(
8905
- path17.join(appDataRoot(appDirName, home), "User", "globalStorage", "state.vscdb"),
10153
+ path18.join(appDataRoot(appDirName, home), "User", "globalStorage", "state.vscdb"),
8906
10154
  { readOnly: true }
8907
10155
  );
8908
10156
  try {
@@ -9044,7 +10292,7 @@ function resolveSessionPreferredModel(db, sessionId, modelMap) {
9044
10292
 
9045
10293
  // src/adapters/qoder-cn.ts
9046
10294
  function parseQoderCnPaths(filePath) {
9047
- const parts = filePath.split(path18.sep);
10295
+ const parts = filePath.split(path19.sep);
9048
10296
  const subagentsIdx = parts.lastIndexOf("subagents");
9049
10297
  let sessionId = "";
9050
10298
  let projectName = "";
@@ -9054,17 +10302,17 @@ function parseQoderCnPaths(filePath) {
9054
10302
  sessionId = parts[subagentsIdx - 1];
9055
10303
  projectName = parts[subagentsIdx - 2];
9056
10304
  const projectsIdx = parts.lastIndexOf("projects");
9057
- configDir2 = parts.slice(0, projectsIdx).join(path18.sep);
9058
- mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path18.sep);
10305
+ configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
10306
+ mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path19.sep);
9059
10307
  } else {
9060
10308
  const filename = parts.at(-1) || "";
9061
- sessionId = path18.basename(filename, ".jsonl");
10309
+ sessionId = path19.basename(filename, ".jsonl");
9062
10310
  projectName = parts.at(-2) || "";
9063
10311
  if (projectName === "transcript") {
9064
10312
  projectName = parts.at(-3) || "";
9065
10313
  }
9066
10314
  const projectsIdx = parts.lastIndexOf("projects");
9067
- configDir2 = parts.slice(0, projectsIdx).join(path18.sep);
10315
+ configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
9068
10316
  }
9069
10317
  return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
9070
10318
  }
@@ -9087,7 +10335,7 @@ function rebuildEventIdentity3(event) {
9087
10335
  }
9088
10336
  async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
9089
10337
  try {
9090
- const content = await readFile11(dynamicTextsPath, "utf8");
10338
+ const content = await readFile12(dynamicTextsPath, "utf8");
9091
10339
  const json = JSON.parse(content);
9092
10340
  const texts = json.texts || {};
9093
10341
  const map = {};
@@ -9103,10 +10351,10 @@ async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
9103
10351
  }
9104
10352
  }
9105
10353
  async function loadQoderCnModelNames(configDir2, home) {
9106
- const map = await parseModelNamesFromDynamicTexts(path18.join(configDir2, ".auth", "dynamic-texts.json"));
10354
+ const map = await parseModelNamesFromDynamicTexts(path19.join(configDir2, ".auth", "dynamic-texts.json"));
9107
10355
  const siblingConfigDir = configDir2.replace(/\.qoder-cn$/, ".qoder");
9108
10356
  if (siblingConfigDir !== configDir2) {
9109
- const siblingMap = await parseModelNamesFromDynamicTexts(path18.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
10357
+ const siblingMap = await parseModelNamesFromDynamicTexts(path19.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
9110
10358
  for (const [key, val] of Object.entries(siblingMap)) {
9111
10359
  if (!(key in map)) {
9112
10360
  map[key] = val;
@@ -9125,15 +10373,15 @@ async function loadQoderCnModelNames(configDir2, home) {
9125
10373
  }
9126
10374
  async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap) {
9127
10375
  const { configDir: configDir2, projectName, sessionId } = parseQoderCnPaths(filePath);
9128
- const segmentsPath = path18.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
10376
+ const segmentsPath = path19.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
9129
10377
  const modelCalls = [];
9130
10378
  try {
9131
- const files = await readdir8(segmentsPath);
10379
+ const files = await readdir9(segmentsPath);
9132
10380
  for (const file of files) {
9133
10381
  if (!file.endsWith(".jsonl")) {
9134
10382
  continue;
9135
10383
  }
9136
- const content = await readFile11(path18.join(segmentsPath, file), "utf8");
10384
+ const content = await readFile12(path19.join(segmentsPath, file), "utf8");
9137
10385
  let currentTurnIsSubagent = false;
9138
10386
  for (const line of content.split("\n").filter(Boolean)) {
9139
10387
  const raw = parseJsonLine(line);
@@ -9167,7 +10415,7 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
9167
10415
  return modelCalls.filter((call) => call.isSubagent === isSubagentSession);
9168
10416
  }
9169
10417
  async function parseQoderCnSessionFile(filePath, options) {
9170
- const text = await readFile11(filePath, "utf8");
10418
+ const text = await readFile12(filePath, "utf8");
9171
10419
  const lines = text.split("\n").filter(Boolean);
9172
10420
  const parsedPaths = parseQoderCnPaths(filePath);
9173
10421
  const { configDir: configDir2 } = parsedPaths;
@@ -9178,7 +10426,7 @@ async function parseQoderCnSessionFile(filePath, options) {
9178
10426
  let cwd;
9179
10427
  let project = projectContext.project;
9180
10428
  let model;
9181
- const home = path18.resolve(stringOption(options.home) || os9.homedir());
10429
+ const home = path19.resolve(stringOption(options.home) || os10.homedir());
9182
10430
  const modelMap = await loadQoderCnModelNames(configDir2, home);
9183
10431
  const isSubagentSession = filePath.includes("subagents");
9184
10432
  const segmentModelCalls = await loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap);
@@ -9213,7 +10461,7 @@ async function parseQoderCnSessionFile(filePath, options) {
9213
10461
  sessionId = stringField(raw, "sessionId") || sessionId;
9214
10462
  state.sessionId = sessionId;
9215
10463
  cwd = stringField(raw, "cwd") || cwd;
9216
- project = projectContext.project || (cwd ? path18.basename(cwd) : project || await qoderCnProjectFromFilePath(filePath, options));
10464
+ project = projectContext.project || (cwd ? path19.basename(cwd) : project || await qoderCnProjectFromFilePath(filePath, options));
9217
10465
  if (!ts) {
9218
10466
  continue;
9219
10467
  }
@@ -9532,10 +10780,11 @@ async function parseQoderCnSessionFile(filePath, options) {
9532
10780
  const { sessionId: parentSessionId, mainTranscriptPath } = parseQoderCnPaths(filePath);
9533
10781
  if (parentSessionId && mainTranscriptPath) {
9534
10782
  const parentSourcePathHash = `sha256:${createStableHash(mainTranscriptPath)}`;
9535
- return validEvents.map((event) => {
10783
+ return withoutSubagentTurnEvents(validEvents).map((event) => {
9536
10784
  const mapped = {
9537
10785
  ...event,
9538
10786
  sessionId: parentSessionId,
10787
+ turnId: void 0,
9539
10788
  refs: {
9540
10789
  ...event.refs,
9541
10790
  sourcePathHash: parentSourcePathHash,
@@ -9548,11 +10797,12 @@ async function parseQoderCnSessionFile(filePath, options) {
9548
10797
  }
9549
10798
  dbModelCalls ??= await loadQoderDbModelCalls("QoderCN", parsedPaths.sessionId, modelMap);
9550
10799
  if (dbModelCalls.rootSessionId) {
9551
- const parentPath = path18.join(path18.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
10800
+ const parentPath = path19.join(path19.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
9552
10801
  const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
9553
- return validEvents.map((event) => rebuildEventIdentity3({
10802
+ return withoutSubagentTurnEvents(validEvents).map((event) => rebuildEventIdentity3({
9554
10803
  ...event,
9555
10804
  sessionId: dbModelCalls.rootSessionId,
10805
+ turnId: void 0,
9556
10806
  refs: {
9557
10807
  ...event.refs,
9558
10808
  sourcePathHash: parentSourcePathHash,
@@ -9700,28 +10950,28 @@ function extractQoderAttachedPrompt(content) {
9700
10950
  }
9701
10951
  async function qoderCnProjectContextFromLines(filePath, lines, options, configDir2) {
9702
10952
  const { projectName: projectDir, sessionId } = parseQoderCnPaths(filePath);
9703
- const isSubagent = filePath.includes(`${path18.sep}subagents${path18.sep}`);
10953
+ const isSubagent = filePath.includes(`${path19.sep}subagents${path19.sep}`);
9704
10954
  const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
9705
10955
  let cwds = [];
9706
10956
  for (const line of lines) {
9707
10957
  const raw = parseJsonLine(line);
9708
10958
  const cwd = raw ? stringField(raw, "cwd") : void 0;
9709
- if (cwd && path18.isAbsolute(cwd)) {
10959
+ if (cwd && path19.isAbsolute(cwd)) {
9710
10960
  cwds.push(cwd);
9711
10961
  }
9712
10962
  }
9713
10963
  if (isSubagent) {
9714
- if (inherited?.cwd && path18.isAbsolute(inherited.cwd)) {
10964
+ if (inherited?.cwd && path19.isAbsolute(inherited.cwd)) {
9715
10965
  cwds = [inherited.cwd];
9716
10966
  } else {
9717
- const parentSessionPath = path18.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
10967
+ const parentSessionPath = path19.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
9718
10968
  try {
9719
- const parentText = await readFile11(parentSessionPath, "utf8");
10969
+ const parentText = await readFile12(parentSessionPath, "utf8");
9720
10970
  const parentCwds = [];
9721
10971
  for (const line of parentText.split("\n").filter(Boolean)) {
9722
10972
  const raw = parseJsonLine(line);
9723
10973
  const cwd = raw ? stringField(raw, "cwd") : void 0;
9724
- if (cwd && path18.isAbsolute(cwd)) {
10974
+ if (cwd && path19.isAbsolute(cwd)) {
9725
10975
  parentCwds.push(cwd);
9726
10976
  }
9727
10977
  }
@@ -9733,7 +10983,7 @@ async function qoderCnProjectContextFromLines(filePath, lines, options, configDi
9733
10983
  }
9734
10984
  }
9735
10985
  const root = await gitRootFromCwds2(cwds) || qoderCnProjectRootFromCwds(projectDir, cwds);
9736
- const project = inherited?.project || (cwds.length > 0 ? path18.basename(cwds[0]) : root ? path18.basename(root) : await qoderCnProjectFromFilePath(filePath, options));
10986
+ const project = inherited?.project || (cwds.length > 0 ? path19.basename(cwds[0]) : root ? path19.basename(root) : await qoderCnProjectFromFilePath(filePath, options));
9737
10987
  return {
9738
10988
  project,
9739
10989
  workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
@@ -9742,15 +10992,15 @@ async function qoderCnProjectContextFromLines(filePath, lines, options, configDi
9742
10992
  async function gitRootFromCwds2(cwds) {
9743
10993
  const seen = /* @__PURE__ */ new Set();
9744
10994
  for (const cwd of cwds) {
9745
- let current = path18.resolve(cwd);
10995
+ let current = path19.resolve(cwd);
9746
10996
  while (!seen.has(current)) {
9747
10997
  seen.add(current);
9748
10998
  try {
9749
- await stat9(path18.join(current, ".git"));
10999
+ await stat10(path19.join(current, ".git"));
9750
11000
  return current;
9751
11001
  } catch {
9752
11002
  }
9753
- const parent = path18.dirname(current);
11003
+ const parent = path19.dirname(current);
9754
11004
  if (parent === current) {
9755
11005
  break;
9756
11006
  }
@@ -9761,12 +11011,12 @@ async function gitRootFromCwds2(cwds) {
9761
11011
  }
9762
11012
  function qoderCnProjectRootFromCwds(projectDir, cwds) {
9763
11013
  for (const cwd of cwds) {
9764
- let current = path18.resolve(cwd);
11014
+ let current = path19.resolve(cwd);
9765
11015
  while (true) {
9766
11016
  if (encodeQoderCnProjectPath(current) === projectDir) {
9767
11017
  return current;
9768
11018
  }
9769
- const parent = path18.dirname(current);
11019
+ const parent = path19.dirname(current);
9770
11020
  if (parent === current) {
9771
11021
  break;
9772
11022
  }
@@ -9776,14 +11026,14 @@ function qoderCnProjectRootFromCwds(projectDir, cwds) {
9776
11026
  return void 0;
9777
11027
  }
9778
11028
  function encodeQoderCnProjectPath(value) {
9779
- return path18.resolve(value).split(path18.sep).join("-").replaceAll("_", "-");
11029
+ return path19.resolve(value).split(path19.sep).join("-").replaceAll("_", "-");
9780
11030
  }
9781
11031
  async function qoderCnProjectFromFilePath(filePath, options) {
9782
- const projectDir = path18.basename(path18.dirname(filePath));
9783
- const home = options ? path18.resolve(stringOption(options.home) || os9.homedir()) : os9.homedir();
11032
+ const projectDir = path19.basename(path19.dirname(filePath));
11033
+ const home = options ? path19.resolve(stringOption(options.home) || os10.homedir()) : os10.homedir();
9784
11034
  const resolved = await resolveQoderCnProjectPath(projectDir, home);
9785
11035
  if (resolved) {
9786
- return path18.basename(resolved);
11036
+ return path19.basename(resolved);
9787
11037
  }
9788
11038
  const homePrefix = `${encodeQoderCnProjectPath(home)}-`;
9789
11039
  if (projectDir.startsWith(homePrefix)) {
@@ -9803,12 +11053,12 @@ async function resolveQoderCnProjectPath(projectDir, home) {
9803
11053
  while (projectDir.startsWith(`${currentEncoded}-`)) {
9804
11054
  let matchedChild;
9805
11055
  try {
9806
- const entries = await readdir8(current, { withFileTypes: true });
11056
+ const entries = await readdir9(current, { withFileTypes: true });
9807
11057
  for (const entry of entries) {
9808
11058
  if (!entry.isDirectory()) {
9809
11059
  continue;
9810
11060
  }
9811
- const candidate = path18.join(current, entry.name);
11061
+ const candidate = path19.join(current, entry.name);
9812
11062
  const encoded = encodeQoderCnProjectPath(candidate);
9813
11063
  if (encoded === projectDir) {
9814
11064
  return candidate;
@@ -9853,9 +11103,9 @@ function hookConfig6() {
9853
11103
  function qoderCnConfigDir(home, env) {
9854
11104
  const override = env?.QODER_CN_CONFIG_DIR;
9855
11105
  if (override && override.trim()) {
9856
- return path18.resolve(override);
11106
+ return path19.resolve(override);
9857
11107
  }
9858
- return path18.join(home, ".qoder-cn");
11108
+ return path19.join(home, ".qoder-cn");
9859
11109
  }
9860
11110
  function createQoderCnAdapter() {
9861
11111
  return {
@@ -9867,27 +11117,27 @@ function createQoderCnAdapter() {
9867
11117
  return qoderCnConfigDir(home, env);
9868
11118
  },
9869
11119
  installedPath(home, env) {
9870
- return path18.join(qoderCnConfigDir(home, env), "settings.json");
11120
+ return path19.join(qoderCnConfigDir(home, env), "settings.json");
9871
11121
  },
9872
11122
  async isInstalled(home, env) {
9873
11123
  return isHooksJsonInstalled(
9874
- path18.join(qoderCnConfigDir(home, env), "settings.json"),
11124
+ path19.join(qoderCnConfigDir(home, env), "settings.json"),
9875
11125
  "vibetime hook --agent qoder-cn"
9876
11126
  );
9877
11127
  },
9878
11128
  installEntries(home, env) {
9879
11129
  return [{
9880
11130
  kind: "hooks-json",
9881
- path: path18.join(qoderCnConfigDir(home, env), "settings.json"),
11131
+ path: path19.join(qoderCnConfigDir(home, env), "settings.json"),
9882
11132
  content: hookConfig6()
9883
11133
  }];
9884
11134
  },
9885
11135
  sourcePaths(home, env) {
9886
11136
  const base = qoderCnConfigDir(home, env);
9887
11137
  return [
9888
- path18.join(base, "projects"),
9889
- path18.join(base, ".qoder.json"),
9890
- path18.join(home, ".qoder.json")
11138
+ path19.join(base, "projects"),
11139
+ path19.join(base, ".qoder.json"),
11140
+ path19.join(home, ".qoder.json")
9891
11141
  ];
9892
11142
  },
9893
11143
  parseSessionFile: parseQoderCnSessionFile
@@ -9896,11 +11146,11 @@ function createQoderCnAdapter() {
9896
11146
 
9897
11147
  // src/adapters/qoder.ts
9898
11148
  import { existsSync } from "node:fs";
9899
- import { readdir as readdir9, readFile as readFile12, stat as stat10 } from "node:fs/promises";
9900
- import os10 from "node:os";
9901
- import path19 from "node:path";
11149
+ import { readdir as readdir10, readFile as readFile13, stat as stat11 } from "node:fs/promises";
11150
+ import os11 from "node:os";
11151
+ import path20 from "node:path";
9902
11152
  function parseQoderPaths(filePath) {
9903
- const parts = filePath.split(path19.sep);
11153
+ const parts = filePath.split(path20.sep);
9904
11154
  const subagentsIdx = parts.lastIndexOf("subagents");
9905
11155
  let sessionId = "";
9906
11156
  let projectName = "";
@@ -9910,17 +11160,17 @@ function parseQoderPaths(filePath) {
9910
11160
  sessionId = parts[subagentsIdx - 1];
9911
11161
  projectName = parts[subagentsIdx - 2];
9912
11162
  const projectsIdx = parts.lastIndexOf("projects");
9913
- configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
9914
- mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path19.sep);
11163
+ configDir2 = parts.slice(0, projectsIdx).join(path20.sep);
11164
+ mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path20.sep);
9915
11165
  } else {
9916
11166
  const filename = parts.at(-1) || "";
9917
- sessionId = path19.basename(filename, ".jsonl");
11167
+ sessionId = path20.basename(filename, ".jsonl");
9918
11168
  projectName = parts.at(-2) || "";
9919
11169
  if (projectName === "transcript") {
9920
11170
  projectName = parts.at(-3) || "";
9921
11171
  }
9922
11172
  const projectsIdx = parts.lastIndexOf("projects");
9923
- configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
11173
+ configDir2 = parts.slice(0, projectsIdx).join(path20.sep);
9924
11174
  }
9925
11175
  return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
9926
11176
  }
@@ -9943,7 +11193,7 @@ function rebuildEventIdentity4(event) {
9943
11193
  }
9944
11194
  async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
9945
11195
  try {
9946
- const content = await readFile12(dynamicTextsPath, "utf8");
11196
+ const content = await readFile13(dynamicTextsPath, "utf8");
9947
11197
  const json = JSON.parse(content);
9948
11198
  const texts = json.texts || {};
9949
11199
  const map = {};
@@ -9959,10 +11209,10 @@ async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
9959
11209
  }
9960
11210
  }
9961
11211
  async function loadQoderModelNames(configDir2, home) {
9962
- const map = await parseModelNamesFromDynamicTexts2(path19.join(configDir2, ".auth", "dynamic-texts.json"));
11212
+ const map = await parseModelNamesFromDynamicTexts2(path20.join(configDir2, ".auth", "dynamic-texts.json"));
9963
11213
  const siblingConfigDir = configDir2.replace(/\.qoder$/, ".qoder-cn");
9964
11214
  if (siblingConfigDir !== configDir2) {
9965
- const siblingMap = await parseModelNamesFromDynamicTexts2(path19.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
11215
+ const siblingMap = await parseModelNamesFromDynamicTexts2(path20.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
9966
11216
  for (const [key, val] of Object.entries(siblingMap)) {
9967
11217
  if (!(key in map)) {
9968
11218
  map[key] = val;
@@ -9970,11 +11220,11 @@ async function loadQoderModelNames(configDir2, home) {
9970
11220
  }
9971
11221
  }
9972
11222
  if (isQwenworkConfigRoot(configDir2)) {
9973
- for (const dir of [path19.join(home, ".qoder"), path19.join(home, ".qoder-cn")]) {
9974
- if (path19.resolve(dir) === path19.resolve(configDir2)) {
11223
+ for (const dir of [path20.join(home, ".qoder"), path20.join(home, ".qoder-cn")]) {
11224
+ if (path20.resolve(dir) === path20.resolve(configDir2)) {
9975
11225
  continue;
9976
11226
  }
9977
- const fallbackMap = await parseModelNamesFromDynamicTexts2(path19.join(dir, ".auth", "dynamic-texts.json"));
11227
+ const fallbackMap = await parseModelNamesFromDynamicTexts2(path20.join(dir, ".auth", "dynamic-texts.json"));
9978
11228
  for (const [key, val] of Object.entries(fallbackMap)) {
9979
11229
  if (!(key in map)) {
9980
11230
  map[key] = val;
@@ -9993,25 +11243,25 @@ async function loadQoderModelNames(configDir2, home) {
9993
11243
  return map;
9994
11244
  }
9995
11245
  function isQwenworkConfigRoot(configDir2) {
9996
- const name = path19.basename(path19.resolve(configDir2));
11246
+ const name = path20.basename(path20.resolve(configDir2));
9997
11247
  if (name === ".qwenworkcn" || name === ".qwenwork") {
9998
11248
  return true;
9999
11249
  }
10000
11250
  const override = process.env.QWENWORK_CONFIG_DIR;
10001
- return Boolean(override && override.trim() && path19.basename(path19.resolve(override)) === name);
11251
+ return Boolean(override && override.trim() && path20.basename(path20.resolve(override)) === name);
10002
11252
  }
10003
11253
  var FAILED_SEGMENT_STOP_REASONS = /* @__PURE__ */ new Set(["cancelled", "canceled", "error", "failed", "refusal"]);
10004
11254
  async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap) {
10005
11255
  const { configDir: configDir2, projectName, sessionId } = parseQoderPaths(filePath);
10006
- const segmentsPath = path19.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
11256
+ const segmentsPath = path20.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
10007
11257
  const modelCalls = [];
10008
11258
  try {
10009
- const files = await readdir9(segmentsPath);
11259
+ const files = await readdir10(segmentsPath);
10010
11260
  for (const file of files) {
10011
11261
  if (!file.endsWith(".jsonl")) {
10012
11262
  continue;
10013
11263
  }
10014
- const content = await readFile12(path19.join(segmentsPath, file), "utf8");
11264
+ const content = await readFile13(path20.join(segmentsPath, file), "utf8");
10015
11265
  let currentTurnIsSubagent = false;
10016
11266
  for (const line of content.split("\n").filter(Boolean)) {
10017
11267
  const raw = parseJsonLine(line);
@@ -10045,7 +11295,7 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
10045
11295
  return modelCalls.filter((call) => call.isSubagent === isSubagentSession);
10046
11296
  }
10047
11297
  async function parseQoderSessionFile(filePath, options) {
10048
- const text = await readFile12(filePath, "utf8");
11298
+ const text = await readFile13(filePath, "utf8");
10049
11299
  const lines = text.split("\n").filter(Boolean);
10050
11300
  const parsedPaths = parseQoderPaths(filePath);
10051
11301
  const { configDir: configDir2 } = parsedPaths;
@@ -10056,7 +11306,7 @@ async function parseQoderSessionFile(filePath, options) {
10056
11306
  let cwd;
10057
11307
  let project = projectContext.project;
10058
11308
  let model;
10059
- const home = path19.resolve(stringOption(options.home) || os10.homedir());
11309
+ const home = path20.resolve(stringOption(options.home) || os11.homedir());
10060
11310
  const modelMap = await loadQoderModelNames(configDir2, home);
10061
11311
  const qwenworkRoot = isQwenworkConfigRoot(configDir2);
10062
11312
  const isSubagentSession = filePath.includes("subagents");
@@ -10092,7 +11342,7 @@ async function parseQoderSessionFile(filePath, options) {
10092
11342
  sessionId = stringField(raw, "sessionId") || sessionId;
10093
11343
  state.sessionId = sessionId;
10094
11344
  cwd = stringField(raw, "cwd") || cwd;
10095
- project = projectContext.project || (cwd ? path19.basename(cwd) : project || await qoderProjectFromFilePath(filePath, options));
11345
+ project = projectContext.project || (cwd ? path20.basename(cwd) : project || await qoderProjectFromFilePath(filePath, options));
10096
11346
  if (!ts) {
10097
11347
  continue;
10098
11348
  }
@@ -10386,10 +11636,11 @@ async function parseQoderSessionFile(filePath, options) {
10386
11636
  const { sessionId: parentSessionId, mainTranscriptPath } = parseQoderPaths(filePath);
10387
11637
  if (parentSessionId && mainTranscriptPath) {
10388
11638
  const parentSourcePathHash = `sha256:${createStableHash(mainTranscriptPath)}`;
10389
- return validEvents.map((event) => {
11639
+ return withoutSubagentTurnEvents(validEvents).map((event) => {
10390
11640
  const mapped = {
10391
11641
  ...event,
10392
11642
  sessionId: parentSessionId,
11643
+ turnId: void 0,
10393
11644
  refs: {
10394
11645
  ...event.refs,
10395
11646
  sourcePathHash: parentSourcePathHash,
@@ -10402,11 +11653,12 @@ async function parseQoderSessionFile(filePath, options) {
10402
11653
  }
10403
11654
  dbModelCalls ??= await loadQoderDbModelCalls("Qoder", parsedPaths.sessionId, modelMap);
10404
11655
  if (dbModelCalls.rootSessionId) {
10405
- const parentPath = path19.join(path19.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
11656
+ const parentPath = path20.join(path20.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
10406
11657
  const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
10407
- return validEvents.map((event) => rebuildEventIdentity4({
11658
+ return withoutSubagentTurnEvents(validEvents).map((event) => rebuildEventIdentity4({
10408
11659
  ...event,
10409
11660
  sessionId: dbModelCalls.rootSessionId,
11661
+ turnId: void 0,
10410
11662
  refs: {
10411
11663
  ...event.refs,
10412
11664
  sourcePathHash: parentSourcePathHash,
@@ -10518,41 +11770,41 @@ function qoderExtractText(value) {
10518
11770
  }
10519
11771
  async function qoderProjectContextFromLines(filePath, lines, options, configDir2) {
10520
11772
  const { projectName: projectDir, sessionId } = parseQoderPaths(filePath);
10521
- const isSubagent = filePath.includes(`${path19.sep}subagents${path19.sep}`);
11773
+ const isSubagent = filePath.includes(`${path20.sep}subagents${path20.sep}`);
10522
11774
  const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
10523
11775
  let cwds = [];
10524
11776
  const workspaceDirs = [];
10525
11777
  for (const line of lines) {
10526
11778
  const raw = parseJsonLine(line);
10527
11779
  const cwd = raw ? stringField(raw, "cwd") : void 0;
10528
- if (cwd && path19.isAbsolute(cwd)) {
11780
+ if (cwd && path20.isAbsolute(cwd)) {
10529
11781
  cwds.push(cwd);
10530
11782
  }
10531
11783
  if (raw && stringField(raw, "type") === "workspace-directories") {
10532
11784
  for (const dir of arrayField5(raw, "directories")) {
10533
- if (typeof dir === "string" && path19.isAbsolute(dir)) {
11785
+ if (typeof dir === "string" && path20.isAbsolute(dir)) {
10534
11786
  workspaceDirs.push(dir);
10535
11787
  }
10536
11788
  }
10537
11789
  }
10538
11790
  }
10539
11791
  if (isSubagent) {
10540
- if (inherited?.cwd && path19.isAbsolute(inherited.cwd)) {
11792
+ if (inherited?.cwd && path20.isAbsolute(inherited.cwd)) {
10541
11793
  cwds = [inherited.cwd];
10542
11794
  } else {
10543
- const parentSessionPath = path19.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
11795
+ const parentSessionPath = path20.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
10544
11796
  try {
10545
- const parentText = await readFile12(parentSessionPath, "utf8");
11797
+ const parentText = await readFile13(parentSessionPath, "utf8");
10546
11798
  const parentCwds = [];
10547
11799
  for (const line of parentText.split("\n").filter(Boolean)) {
10548
11800
  const raw = parseJsonLine(line);
10549
11801
  const cwd = raw ? stringField(raw, "cwd") : void 0;
10550
- if (cwd && path19.isAbsolute(cwd)) {
11802
+ if (cwd && path20.isAbsolute(cwd)) {
10551
11803
  parentCwds.push(cwd);
10552
11804
  }
10553
11805
  if (workspaceDirs.length === 0 && raw && stringField(raw, "type") === "workspace-directories") {
10554
11806
  for (const dir of arrayField5(raw, "directories")) {
10555
- if (typeof dir === "string" && path19.isAbsolute(dir)) {
11807
+ if (typeof dir === "string" && path20.isAbsolute(dir)) {
10556
11808
  workspaceDirs.push(dir);
10557
11809
  }
10558
11810
  }
@@ -10572,29 +11824,29 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
10572
11824
  }
10573
11825
  }
10574
11826
  const root = await gitRootFromCwds3(cwds) || qoderProjectRootFromCwds(projectDir, cwds);
10575
- const project = inherited?.project || (cwds.length > 0 ? path19.basename(cwds[0]) : root ? path19.basename(root) : await qoderProjectFromFilePath(filePath, options));
11827
+ const project = inherited?.project || (cwds.length > 0 ? path20.basename(cwds[0]) : root ? path20.basename(root) : await qoderProjectFromFilePath(filePath, options));
10576
11828
  return {
10577
11829
  project,
10578
11830
  workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
10579
11831
  };
10580
11832
  }
10581
11833
  function pathInsideDir(candidate, dir) {
10582
- const resolvedDir = path19.resolve(dir);
10583
- const resolved = path19.resolve(candidate);
10584
- return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${path19.sep}`);
11834
+ const resolvedDir = path20.resolve(dir);
11835
+ const resolved = path20.resolve(candidate);
11836
+ return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${path20.sep}`);
10585
11837
  }
10586
11838
  async function gitRootFromCwds3(cwds) {
10587
11839
  const seen = /* @__PURE__ */ new Set();
10588
11840
  for (const cwd of cwds) {
10589
- let current = path19.resolve(cwd);
11841
+ let current = path20.resolve(cwd);
10590
11842
  while (!seen.has(current)) {
10591
11843
  seen.add(current);
10592
11844
  try {
10593
- await stat10(path19.join(current, ".git"));
11845
+ await stat11(path20.join(current, ".git"));
10594
11846
  return current;
10595
11847
  } catch {
10596
11848
  }
10597
- const parent = path19.dirname(current);
11849
+ const parent = path20.dirname(current);
10598
11850
  if (parent === current) {
10599
11851
  break;
10600
11852
  }
@@ -10605,12 +11857,12 @@ async function gitRootFromCwds3(cwds) {
10605
11857
  }
10606
11858
  function qoderProjectRootFromCwds(projectDir, cwds) {
10607
11859
  for (const cwd of cwds) {
10608
- let current = path19.resolve(cwd);
11860
+ let current = path20.resolve(cwd);
10609
11861
  while (true) {
10610
11862
  if (qoderEncodedVariants(current).includes(projectDir)) {
10611
11863
  return current;
10612
11864
  }
10613
- const parent = path19.dirname(current);
11865
+ const parent = path20.dirname(current);
10614
11866
  if (parent === current) {
10615
11867
  break;
10616
11868
  }
@@ -10620,7 +11872,7 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
10620
11872
  return void 0;
10621
11873
  }
10622
11874
  function rawQoderProjectPath(value) {
10623
- return path19.resolve(value).split(path19.sep).join("-");
11875
+ return path20.resolve(value).split(path20.sep).join("-");
10624
11876
  }
10625
11877
  function qoderEncodedVariants(value) {
10626
11878
  const raw = rawQoderProjectPath(value);
@@ -10641,11 +11893,11 @@ function qoderEncodedProjectSuffix(projectDir, home) {
10641
11893
  return void 0;
10642
11894
  }
10643
11895
  async function qoderProjectFromFilePath(filePath, options) {
10644
- const projectDir = path19.basename(path19.dirname(filePath));
10645
- const home = options ? path19.resolve(stringOption(options.home) || os10.homedir()) : os10.homedir();
11896
+ const projectDir = path20.basename(path20.dirname(filePath));
11897
+ const home = options ? path20.resolve(stringOption(options.home) || os11.homedir()) : os11.homedir();
10646
11898
  const resolved = await resolveQoderProjectPath(projectDir, home);
10647
11899
  if (resolved) {
10648
- return path19.basename(resolved);
11900
+ return path20.basename(resolved);
10649
11901
  }
10650
11902
  const suffix = qoderEncodedProjectSuffix(projectDir, home);
10651
11903
  if (suffix) {
@@ -10666,12 +11918,12 @@ async function resolveQoderProjectPath(projectDir, home) {
10666
11918
  while (currentEncodedVariants.some((prefix) => projectDir.startsWith(`${prefix}-`))) {
10667
11919
  let matchedChild;
10668
11920
  try {
10669
- const entries = await readdir9(current, { withFileTypes: true });
11921
+ const entries = await readdir10(current, { withFileTypes: true });
10670
11922
  for (const entry of entries) {
10671
11923
  if (!entry.isDirectory()) {
10672
11924
  continue;
10673
11925
  }
10674
- const candidate = path19.join(current, entry.name);
11926
+ const candidate = path20.join(current, entry.name);
10675
11927
  const candidateVariants = qoderEncodedVariants(candidate);
10676
11928
  if (candidateVariants.includes(projectDir)) {
10677
11929
  return candidate;
@@ -10716,19 +11968,19 @@ function hookConfig7() {
10716
11968
  function qoderConfigDir(home, env) {
10717
11969
  const override = env?.QODER_CONFIG_DIR;
10718
11970
  if (override && override.trim()) {
10719
- return path19.resolve(override);
11971
+ return path20.resolve(override);
10720
11972
  }
10721
- return path19.join(home, ".qoder");
11973
+ return path20.join(home, ".qoder");
10722
11974
  }
10723
11975
  function qwenworkConfigDir(home, env) {
10724
11976
  const override = env?.QWENWORK_CONFIG_DIR;
10725
11977
  if (override && override.trim()) {
10726
- return path19.resolve(override);
11978
+ return path20.resolve(override);
10727
11979
  }
10728
- return path19.join(home, ".qwenworkcn");
11980
+ return path20.join(home, ".qwenworkcn");
10729
11981
  }
10730
11982
  function qoderConfigDirs(home, env) {
10731
- return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) => path19.resolve(dir)))];
11983
+ return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) => path20.resolve(dir)))];
10732
11984
  }
10733
11985
  function createQoderAdapter() {
10734
11986
  return {
@@ -10740,11 +11992,11 @@ function createQoderAdapter() {
10740
11992
  return qoderConfigDir(home, env);
10741
11993
  },
10742
11994
  installedPath(home, env) {
10743
- return path19.join(qoderConfigDir(home, env), "settings.json");
11995
+ return path20.join(qoderConfigDir(home, env), "settings.json");
10744
11996
  },
10745
11997
  async isInstalled(home, env) {
10746
11998
  return isHooksJsonInstalled(
10747
- path19.join(qoderConfigDir(home, env), "settings.json"),
11999
+ path20.join(qoderConfigDir(home, env), "settings.json"),
10748
12000
  "vibetime hook --agent qoder"
10749
12001
  );
10750
12002
  },
@@ -10753,16 +12005,16 @@ function createQoderAdapter() {
10753
12005
  const targets = [primary, ...variants.filter((dir) => existsSync(dir))];
10754
12006
  return targets.map((base) => ({
10755
12007
  kind: "hooks-json",
10756
- path: path19.join(base, "settings.json"),
12008
+ path: path20.join(base, "settings.json"),
10757
12009
  content: hookConfig7()
10758
12010
  }));
10759
12011
  },
10760
12012
  sourcePaths(home, env) {
10761
- const paths = qoderConfigDirs(home, env).map((base2) => path19.join(base2, "projects"));
12013
+ const paths = qoderConfigDirs(home, env).map((base2) => path20.join(base2, "projects"));
10762
12014
  const base = qoderConfigDir(home, env);
10763
12015
  paths.push(
10764
- path19.join(base, ".qoder.json"),
10765
- path19.join(home, ".qoder.json")
12016
+ path20.join(base, ".qoder.json"),
12017
+ path20.join(home, ".qoder.json")
10766
12018
  );
10767
12019
  return paths;
10768
12020
  },
@@ -10798,27 +12050,27 @@ function normalizeId(id) {
10798
12050
  }
10799
12051
 
10800
12052
  // src/adapters/workbuddy.ts
10801
- import { readdir as readdir10, readFile as readFile13, stat as stat11 } from "node:fs/promises";
10802
- import path20 from "node:path";
12053
+ import { readdir as readdir11, readFile as readFile14, stat as stat12 } from "node:fs/promises";
12054
+ import path21 from "node:path";
10803
12055
  function workbuddyProjectsDir(home, env) {
10804
12056
  const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
10805
12057
  if (override && override.trim()) {
10806
- return path20.resolve(override, override.endsWith("projects") ? "" : "projects");
12058
+ return path21.resolve(override, override.endsWith("projects") ? "" : "projects");
10807
12059
  }
10808
- return path20.join(home, ".workbuddy", "projects");
12060
+ return path21.join(home, ".workbuddy", "projects");
10809
12061
  }
10810
12062
  function workbuddyBaseDir(home, env) {
10811
12063
  const override = env?.WORKBUDDY_HOME;
10812
12064
  if (override && override.trim()) {
10813
- return path20.resolve(override);
12065
+ return path21.resolve(override);
10814
12066
  }
10815
- return path20.join(home, ".workbuddy");
12067
+ return path21.join(home, ".workbuddy");
10816
12068
  }
10817
12069
  function projectFromCwd(cwd, fallback) {
10818
12070
  if (!cwd) {
10819
12071
  return fallback;
10820
12072
  }
10821
- return path20.basename(cwd) || fallback;
12073
+ return path21.basename(cwd) || fallback;
10822
12074
  }
10823
12075
  function sourceHash(filePath) {
10824
12076
  return `sha256:${createStableHash(filePath)}`;
@@ -10936,7 +12188,7 @@ function toolCallFailed(record) {
10936
12188
  return status === "failed" || status === "incomplete" || record.is_error === true || providerData.error != null || providerData.isError === true;
10937
12189
  }
10938
12190
  async function readWorkbuddyLines(filePath) {
10939
- const text = await readFile13(filePath, "utf8");
12191
+ const text = await readFile14(filePath, "utf8");
10940
12192
  return text.split(/\r?\n/).map((line, index) => {
10941
12193
  if (!line.trim()) {
10942
12194
  return void 0;
@@ -10956,8 +12208,8 @@ async function parseWorkbuddySessionFile(filePath, options) {
10956
12208
  }
10957
12209
  const events = [];
10958
12210
  const first = lines[0].record;
10959
- const sessionId = stringField(first, "sessionId") || path20.basename(filePath, ".jsonl");
10960
- const fallbackProject = path20.basename(path20.dirname(filePath));
12211
+ const sessionId = stringField(first, "sessionId") || path21.basename(filePath, ".jsonl");
12212
+ const fallbackProject = path21.basename(path21.dirname(filePath));
10961
12213
  const cwd = lines.map((line) => stringField(line.record, "cwd")).find(Boolean);
10962
12214
  const project = projectFromCwd(cwd, fallbackProject);
10963
12215
  const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
@@ -11241,17 +12493,17 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
11241
12493
  const base = sourceRoot || workbuddyProjectsDir(home, env);
11242
12494
  const files = [];
11243
12495
  try {
11244
- const projects = await readdir10(base, { withFileTypes: true });
12496
+ const projects = await readdir11(base, { withFileTypes: true });
11245
12497
  for (const project of projects) {
11246
12498
  if (!project.isDirectory()) {
11247
12499
  continue;
11248
12500
  }
11249
- const projectDir = path20.join(base, project.name);
11250
- const entries = await readdir10(projectDir, { withFileTypes: true });
12501
+ const projectDir = path21.join(base, project.name);
12502
+ const entries = await readdir11(projectDir, { withFileTypes: true });
11251
12503
  for (const entry of entries) {
11252
12504
  if (entry.isFile() && entry.name.endsWith(".jsonl")) {
11253
- const filePath = path20.join(projectDir, entry.name);
11254
- const info = await stat11(filePath);
12505
+ const filePath = path21.join(projectDir, entry.name);
12506
+ const info = await stat12(filePath);
11255
12507
  files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
11256
12508
  }
11257
12509
  }
@@ -11288,18 +12540,18 @@ function createWorkbuddyAdapter() {
11288
12540
  return workbuddyProjectsDir(home, env);
11289
12541
  },
11290
12542
  installedPath(home, env) {
11291
- return path20.join(workbuddyBaseDir(home, env), "settings.json");
12543
+ return path21.join(workbuddyBaseDir(home, env), "settings.json");
11292
12544
  },
11293
12545
  async isInstalled(home, env) {
11294
12546
  return isHooksJsonInstalled(
11295
- path20.join(workbuddyBaseDir(home, env), "settings.json"),
12547
+ path21.join(workbuddyBaseDir(home, env), "settings.json"),
11296
12548
  "vibetime hook --agent workbuddy"
11297
12549
  );
11298
12550
  },
11299
12551
  installEntries(home, env) {
11300
12552
  return [{
11301
12553
  kind: "hooks-json",
11302
- path: path20.join(workbuddyBaseDir(home, env), "settings.json"),
12554
+ path: path21.join(workbuddyBaseDir(home, env), "settings.json"),
11303
12555
  content: hookConfig8()
11304
12556
  }];
11305
12557
  },
@@ -11312,26 +12564,26 @@ function createWorkbuddyAdapter() {
11312
12564
 
11313
12565
  // src/adapters/zcode.ts
11314
12566
  import { execFile } from "node:child_process";
11315
- import { readFile as readFile14, stat as stat12 } from "node:fs/promises";
11316
- import path21 from "node:path";
12567
+ import { readFile as readFile15, stat as stat13 } from "node:fs/promises";
12568
+ import path22 from "node:path";
11317
12569
  import { promisify as promisify2 } from "node:util";
11318
12570
  init_fs();
11319
12571
  var execFileAsync = promisify2(execFile);
11320
12572
  function zcodeCliDir(home, env) {
11321
12573
  const override = env?.ZCODE_CLI_DIR || env?.ZCODE_HOME;
11322
12574
  if (override && override.trim()) {
11323
- return path21.resolve(override, override.endsWith("cli") ? "" : "cli");
12575
+ return path22.resolve(override, override.endsWith("cli") ? "" : "cli");
11324
12576
  }
11325
- return path21.join(home, ".zcode", "cli");
12577
+ return path22.join(home, ".zcode", "cli");
11326
12578
  }
11327
12579
  function zcodeDbPath(home, env) {
11328
- return path21.join(zcodeCliDir(home, env), "db", "db.sqlite");
12580
+ return path22.join(zcodeCliDir(home, env), "db", "db.sqlite");
11329
12581
  }
11330
12582
  var providerNameCache = null;
11331
12583
  async function loadProviderNames(configPath2) {
11332
12584
  let fileMtime = 0;
11333
12585
  try {
11334
- const info = await stat12(configPath2);
12586
+ const info = await stat13(configPath2);
11335
12587
  fileMtime = info.mtimeMs;
11336
12588
  } catch {
11337
12589
  return /* @__PURE__ */ new Map();
@@ -11341,7 +12593,7 @@ async function loadProviderNames(configPath2) {
11341
12593
  }
11342
12594
  const map = /* @__PURE__ */ new Map();
11343
12595
  try {
11344
- const raw = await readFile14(configPath2, "utf-8");
12596
+ const raw = await readFile15(configPath2, "utf-8");
11345
12597
  const config = JSON.parse(raw);
11346
12598
  const providers = config?.provider;
11347
12599
  if (isPlainObject(providers)) {
@@ -11359,7 +12611,7 @@ function sourceHash2(filePath) {
11359
12611
  return `sha256:${createStableHash(filePath)}`;
11360
12612
  }
11361
12613
  function projectFromDirectory(directory) {
11362
- return directory ? path21.basename(directory) || "zcode" : "zcode";
12614
+ return directory ? path22.basename(directory) || "zcode" : "zcode";
11363
12615
  }
11364
12616
  function isoFromMs(value) {
11365
12617
  return timestampFrom(typeof value === "number" ? value : Number(value));
@@ -11543,16 +12795,16 @@ async function parseZCodeDb(filePath, options) {
11543
12795
  if (rows.length === 0) {
11544
12796
  return [];
11545
12797
  }
11546
- let candidate = path21.resolve(filePath);
12798
+ let candidate = path22.resolve(filePath);
11547
12799
  let configPath2 = "";
11548
12800
  for (let i = 0; i < 12; i++) {
11549
- const probe = path21.join(candidate, ".zcode", "v2", "config.json");
12801
+ const probe = path22.join(candidate, ".zcode", "v2", "config.json");
11550
12802
  try {
11551
- await stat12(probe);
12803
+ await stat13(probe);
11552
12804
  configPath2 = probe;
11553
12805
  break;
11554
12806
  } catch {
11555
- const parent = path21.dirname(candidate);
12807
+ const parent = path22.dirname(candidate);
11556
12808
  if (parent === candidate) break;
11557
12809
  candidate = parent;
11558
12810
  }
@@ -11772,9 +13024,9 @@ async function parseZCodeDb(filePath, options) {
11772
13024
  }
11773
13025
  async function zcodeBackfillFiles(sourceRoot, home, env) {
11774
13026
  const candidate = sourceRoot || zcodeDbPath(home, env);
11775
- const filePath = candidate.endsWith(".sqlite") ? candidate : path21.join(candidate, "db", "db.sqlite");
13027
+ const filePath = candidate.endsWith(".sqlite") ? candidate : path22.join(candidate, "db", "db.sqlite");
11776
13028
  try {
11777
- const info = await stat12(filePath);
13029
+ const info = await stat13(filePath);
11778
13030
  return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
11779
13031
  } catch {
11780
13032
  return [];
@@ -11806,50 +13058,50 @@ function createZCodeAdapter() {
11806
13058
  }
11807
13059
 
11808
13060
  // src/adapters/zed.ts
11809
- import os11 from "node:os";
11810
- import path22 from "node:path";
13061
+ import os12 from "node:os";
13062
+ import path23 from "node:path";
11811
13063
  function zedThreadsCandidates(home, env) {
11812
13064
  const candidates = [];
11813
13065
  const platform2 = process.platform;
11814
13066
  if (platform2 === "darwin") {
11815
- candidates.push(path22.join(home, "Library", "Application Support", "Zed", "threads", "threads.db"));
13067
+ candidates.push(path23.join(home, "Library", "Application Support", "Zed", "threads", "threads.db"));
11816
13068
  } else if (platform2 === "win32") {
11817
13069
  const appdata = env?.APPDATA;
11818
13070
  if (appdata && appdata.trim()) {
11819
- candidates.push(path22.join(path22.resolve(appdata), "Zed", "threads", "threads.db"));
13071
+ candidates.push(path23.join(path23.resolve(appdata), "Zed", "threads", "threads.db"));
11820
13072
  }
11821
- candidates.push(path22.join(home, "AppData", "Roaming", "Zed", "threads", "threads.db"));
13073
+ candidates.push(path23.join(home, "AppData", "Roaming", "Zed", "threads", "threads.db"));
11822
13074
  } else {
11823
13075
  const xdgData = env?.XDG_DATA_HOME;
11824
13076
  if (xdgData && xdgData.trim()) {
11825
- candidates.push(path22.join(path22.resolve(xdgData), "zed", "threads", "threads.db"));
13077
+ candidates.push(path23.join(path23.resolve(xdgData), "zed", "threads", "threads.db"));
11826
13078
  }
11827
- candidates.push(path22.join(home, ".local", "share", "zed", "threads", "threads.db"));
13079
+ candidates.push(path23.join(home, ".local", "share", "zed", "threads", "threads.db"));
11828
13080
  const xdgConfig = env?.XDG_CONFIG_HOME;
11829
13081
  if (xdgConfig && xdgConfig.trim()) {
11830
- candidates.push(path22.join(path22.resolve(xdgConfig), "zed", "threads", "threads.db"));
13082
+ candidates.push(path23.join(path23.resolve(xdgConfig), "zed", "threads", "threads.db"));
11831
13083
  }
11832
- candidates.push(path22.join(home, ".config", "zed", "threads", "threads.db"));
13084
+ candidates.push(path23.join(home, ".config", "zed", "threads", "threads.db"));
11833
13085
  }
11834
13086
  return candidates;
11835
13087
  }
11836
13088
  function zedConfigDir(home, env) {
11837
13089
  const platform2 = process.platform;
11838
13090
  if (platform2 === "darwin") {
11839
- return path22.join(home, "Library", "Application Support", "Zed");
13091
+ return path23.join(home, "Library", "Application Support", "Zed");
11840
13092
  }
11841
13093
  if (platform2 === "win32") {
11842
13094
  const appdata = env?.APPDATA;
11843
13095
  if (appdata && appdata.trim()) {
11844
- return path22.join(path22.resolve(appdata), "Zed");
13096
+ return path23.join(path23.resolve(appdata), "Zed");
11845
13097
  }
11846
- return path22.join(home, "AppData", "Roaming", "Zed");
13098
+ return path23.join(home, "AppData", "Roaming", "Zed");
11847
13099
  }
11848
13100
  const xdgConfig = env?.XDG_CONFIG_HOME;
11849
13101
  if (xdgConfig && xdgConfig.trim()) {
11850
- return path22.join(path22.resolve(xdgConfig), "zed");
13102
+ return path23.join(path23.resolve(xdgConfig), "zed");
11851
13103
  }
11852
- return path22.join(home, ".config", "zed");
13104
+ return path23.join(home, ".config", "zed");
11853
13105
  }
11854
13106
  function baseZedEvent(event) {
11855
13107
  return {
@@ -11913,7 +13165,7 @@ async function parseZedSessionFile(dbPath, options) {
11913
13165
  const folderRaw = row.folder_paths || "";
11914
13166
  const folder = folderRaw.split(/[\n,]/).map((s) => s.trim()).find(Boolean);
11915
13167
  const cwd = folder || void 0;
11916
- const project = cwd ? path22.basename(cwd) : row.summary ? row.summary.slice(0, 40) : void 0;
13168
+ const project = cwd ? path23.basename(cwd) : row.summary ? row.summary.slice(0, 40) : void 0;
11917
13169
  let json;
11918
13170
  try {
11919
13171
  const bytes = row.data_type === "zstd" ? decompress2(new Uint8Array(row.data)) : new Uint8Array(row.data);
@@ -12158,20 +13410,20 @@ async function parseZedSessionFile(dbPath, options) {
12158
13410
  }
12159
13411
  return events.filter((event) => matchesBackfillFilters(event, options));
12160
13412
  }
12161
- async function zedBackfillFiles(sourceRoot, home = os11.homedir(), env) {
12162
- const { stat: stat15 } = await import("node:fs/promises");
13413
+ async function zedBackfillFiles(sourceRoot, home = os12.homedir(), env) {
13414
+ const { stat: stat16 } = await import("node:fs/promises");
12163
13415
  if (sourceRoot) {
12164
13416
  if (!sourceRoot.endsWith(".db")) {
12165
13417
  return [];
12166
13418
  }
12167
- const info = await stat15(sourceRoot).catch(() => null);
13419
+ const info = await stat16(sourceRoot).catch(() => null);
12168
13420
  if (!info) {
12169
13421
  return [];
12170
13422
  }
12171
13423
  return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
12172
13424
  }
12173
13425
  for (const candidatePath of zedThreadsCandidates(home, env)) {
12174
- const info = await stat15(candidatePath).catch(() => null);
13426
+ const info = await stat16(candidatePath).catch(() => null);
12175
13427
  if (info) {
12176
13428
  return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
12177
13429
  }
@@ -12188,7 +13440,7 @@ function createZedAdapter() {
12188
13440
  return zedConfigDir(home, env);
12189
13441
  },
12190
13442
  installedPath(home, env) {
12191
- return path22.join(zedConfigDir(home, env), "vibetime-marker");
13443
+ return path23.join(zedConfigDir(home, env), "vibetime-marker");
12192
13444
  },
12193
13445
  async isInstalled() {
12194
13446
  return false;
@@ -12563,6 +13815,10 @@ async function installEntry(entry, options) {
12563
13815
  await mergeHooksJson(entry.path, entry.content, options);
12564
13816
  return;
12565
13817
  }
13818
+ if (entry.kind === "cursor-hooks-json" && typeof entry.content === "object") {
13819
+ await mergeCursorHooksFile(entry.path, entry.content, options);
13820
+ return;
13821
+ }
12566
13822
  if (entry.kind === "hooks-toml" && typeof entry.content === "object") {
12567
13823
  await mergeHooksToml(entry.path, entry.content, options);
12568
13824
  return;
@@ -12577,6 +13833,10 @@ async function uninstallEntry(entry, options) {
12577
13833
  await uninstallHooksToml(entry.path, entry.content, options);
12578
13834
  return;
12579
13835
  }
13836
+ if (entry.kind === "cursor-hooks-json" && typeof entry.content === "object") {
13837
+ await uninstallCursorHooksFile(entry.path, entry.content, options);
13838
+ return;
13839
+ }
12580
13840
  if (entry.kind === "hooks-json" && typeof entry.content === "object") {
12581
13841
  await uninstallHooksJson(entry.path, entry.content, options);
12582
13842
  return;
@@ -12609,6 +13869,67 @@ async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
12609
13869
  await writeFile5(filePath, nextText, "utf8");
12610
13870
  onWrite(`Installed ${filePath}`);
12611
13871
  }
13872
+ async function mergeCursorHooksFile(filePath, content, { dryRun, force, onWrite }) {
13873
+ const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
13874
+ const pathMod = await import("node:path");
13875
+ if (dryRun) {
13876
+ onWrite(`Would merge ${filePath}`);
13877
+ return;
13878
+ }
13879
+ const existingText = await readTextIfExists(filePath);
13880
+ const existing = existingText ? JSON.parse(existingText) : {};
13881
+ if (existingText !== null && !isPlainObject(existing) && !force) {
13882
+ throw new Error(
13883
+ `Refusing to update non-object JSON file: ${filePath}. Re-run with --force if this is intentional.`
13884
+ );
13885
+ }
13886
+ const merged = mergeCursorHooksJson(existing, content);
13887
+ const nextText = `${JSON.stringify(merged, null, 2)}
13888
+ `;
13889
+ if (existingText === nextText) {
13890
+ onWrite(`Already installed ${filePath}`);
13891
+ return;
13892
+ }
13893
+ await mkdir6(pathMod.dirname(filePath), { recursive: true });
13894
+ await writeFile5(filePath, nextText, "utf8");
13895
+ onWrite(`Installed ${filePath}`);
13896
+ }
13897
+ async function uninstallCursorHooksFile(filePath, content, { dryRun, onWrite }) {
13898
+ const existingText = await readTextIfExists(filePath);
13899
+ if (existingText === null) {
13900
+ onWrite(`Already uninstalled ${filePath}`);
13901
+ return;
13902
+ }
13903
+ let existing;
13904
+ try {
13905
+ existing = JSON.parse(existingText);
13906
+ } catch {
13907
+ onWrite(`Skipped non-JSON file ${filePath}`);
13908
+ return;
13909
+ }
13910
+ if (!isPlainObject(existing)) {
13911
+ onWrite(`Skipped non-object JSON file ${filePath}`);
13912
+ return;
13913
+ }
13914
+ const commands = new Set(collectCursorHookCommands(content));
13915
+ if (commands.size === 0) {
13916
+ onWrite(`Already uninstalled ${filePath}`);
13917
+ return;
13918
+ }
13919
+ const { next, changed } = stripCursorHookCommands(existing, commands);
13920
+ if (!changed) {
13921
+ onWrite(`Already uninstalled ${filePath}`);
13922
+ return;
13923
+ }
13924
+ if (dryRun) {
13925
+ onWrite(`Would uninstall ${filePath}`);
13926
+ return;
13927
+ }
13928
+ const { writeFile: writeFile5 } = await import("node:fs/promises");
13929
+ await writeFile5(filePath, `${JSON.stringify(next, null, 2)}
13930
+ `, "utf8");
13931
+ onWrite(`Uninstalled ${filePath}`);
13932
+ }
12612
13933
  function mergeAgyHooksJson(existing, addition) {
12613
13934
  const merged = structuredClone(isPlainObject(existing) ? existing : {});
12614
13935
  const additionObject = isPlainObject(addition) ? addition : {};
@@ -12896,15 +14217,15 @@ async function uninstallGeneratedFile(filePath, { dryRun, onWrite }) {
12896
14217
  import { randomUUID } from "node:crypto";
12897
14218
  import { existsSync as existsSync2, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
12898
14219
  import { homedir, hostname } from "node:os";
12899
- import path23 from "node:path";
14220
+ import path24 from "node:path";
12900
14221
  function configDir(home = homedir()) {
12901
- return path23.join(home, ".vibetime");
14222
+ return path24.join(home, ".vibetime");
12902
14223
  }
12903
14224
  function configPath(home = homedir()) {
12904
- return path23.join(configDir(home), "config.json");
14225
+ return path24.join(configDir(home), "config.json");
12905
14226
  }
12906
14227
  function machineIdPath(home = homedir()) {
12907
- return path23.join(configDir(home), "machine-id");
14228
+ return path24.join(configDir(home), "machine-id");
12908
14229
  }
12909
14230
  function readConfig(home = homedir()) {
12910
14231
  const file = configPath(home);
@@ -12951,15 +14272,15 @@ function defaultMachineName() {
12951
14272
  init_fs();
12952
14273
 
12953
14274
  // src/lib/logger.ts
12954
- import { appendFile, mkdir as mkdir4, rename, stat as stat13 } from "node:fs/promises";
14275
+ import { appendFile, mkdir as mkdir4, rename, stat as stat14 } from "node:fs/promises";
12955
14276
  import { homedir as homedir2 } from "node:os";
12956
- import path24 from "node:path";
14277
+ import path25 from "node:path";
12957
14278
  var MAX_BYTES = 1 * 1024 * 1024;
12958
14279
  function logDir(home = homedir2()) {
12959
- return path24.join(home, ".vibetime", "logs");
14280
+ return path25.join(home, ".vibetime", "logs");
12960
14281
  }
12961
14282
  function logPath(home = homedir2(), name = "cli.log") {
12962
- return path24.join(logDir(home), name);
14283
+ return path25.join(logDir(home), name);
12963
14284
  }
12964
14285
  function serializeError(error) {
12965
14286
  if (error instanceof Error) {
@@ -12969,7 +14290,7 @@ function serializeError(error) {
12969
14290
  }
12970
14291
  async function rotateIfNeeded(file) {
12971
14292
  try {
12972
- const info = await stat13(file);
14293
+ const info = await stat14(file);
12973
14294
  if (info.size > MAX_BYTES) {
12974
14295
  await rename(file, `${file}.1`).catch(() => {
12975
14296
  });
@@ -13134,8 +14455,8 @@ function buildHeaders(token, machine) {
13134
14455
  ...machine?.platform ? { "x-machine-platform": machine.platform } : {}
13135
14456
  };
13136
14457
  }
13137
- function joinUrl(base, path26) {
13138
- return new URL(path26, base.endsWith("/") ? base : `${base}/`).toString();
14458
+ function joinUrl(base, path27) {
14459
+ return new URL(path27, base.endsWith("/") ? base : `${base}/`).toString();
13139
14460
  }
13140
14461
  async function postRollupBatch(remote, rollups, options = {}) {
13141
14462
  const response = await remote.fetchImpl(joinUrl(remote.baseUrl, "/v3/agent/ingest"), {
@@ -13205,7 +14526,7 @@ async function deleteMachine(remote, id) {
13205
14526
  }
13206
14527
 
13207
14528
  // src/lib/types.ts
13208
- var BACKFILL_STATE_SCHEMA_VERSION = 6;
14529
+ var BACKFILL_STATE_SCHEMA_VERSION = 7;
13209
14530
 
13210
14531
  // src/cli.ts
13211
14532
  function createRegistry() {
@@ -13225,6 +14546,7 @@ function createRegistry() {
13225
14546
  registry.register(createGrokBuildAdapter());
13226
14547
  registry.register(createZedAdapter());
13227
14548
  registry.register(createKimiCodeAdapter());
14549
+ registry.register(createCursorAdapter());
13228
14550
  return registry;
13229
14551
  }
13230
14552
  var defaultContext = {
@@ -13782,6 +15104,9 @@ async function listBackfillSourceFiles(source, options, ctx) {
13782
15104
  if (source.id === "zed") {
13783
15105
  return zedBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
13784
15106
  }
15107
+ if (source.id === "cursor") {
15108
+ return cursorBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
15109
+ }
13785
15110
  if (source.id === "pi") {
13786
15111
  return piBackfillFiles(stringOption(options["source-root"]), resolveHome3(options, ctx), ctx.env);
13787
15112
  }
@@ -13789,7 +15114,7 @@ async function listBackfillSourceFiles(source, options, ctx) {
13789
15114
  const fileLists = await Promise.all(roots.map((r) => listJsonlFiles(r)));
13790
15115
  const files = fileLists.flat().sort().slice(0, numberOption(options.limit) || void 0);
13791
15116
  return Promise.all(files.map(async (filePath) => {
13792
- const info = await stat14(filePath);
15117
+ const info = await stat15(filePath);
13793
15118
  return { path: filePath, modifiedAt: info.mtime.toISOString() };
13794
15119
  }));
13795
15120
  }
@@ -14121,13 +15446,13 @@ function selectBackfillFilesForImport(files, watermarkTs) {
14121
15446
  return picked;
14122
15447
  }
14123
15448
  function backfillIncrementalStatePath(home) {
14124
- return path25.join(home, ".vibetime", "backfill-state.json");
15449
+ return path26.join(home, ".vibetime", "backfill-state.json");
14125
15450
  }
14126
15451
  function syncLocalTriggerStatePath(home) {
14127
- return path25.join(home, ".vibetime", "sync-local-trigger.json");
15452
+ return path26.join(home, ".vibetime", "sync-local-trigger.json");
14128
15453
  }
14129
15454
  function syncLocalTriggerLockPath(home) {
14130
- return path25.join(home, ".vibetime", "sync-local-trigger.lock");
15455
+ return path26.join(home, ".vibetime", "sync-local-trigger.lock");
14131
15456
  }
14132
15457
  function backfillRemoteKey(baseUrl) {
14133
15458
  try {
@@ -14189,7 +15514,7 @@ async function readBackfillIncrementalStateFile(home, ctx) {
14189
15514
  }
14190
15515
  async function writeBackfillIncrementalStateFile(home, file) {
14191
15516
  const statePath = backfillIncrementalStatePath(home);
14192
- await mkdir5(path25.dirname(statePath), { recursive: true });
15517
+ await mkdir5(path26.dirname(statePath), { recursive: true });
14193
15518
  await writeFile4(statePath, `${JSON.stringify(file, null, 2)}
14194
15519
  `, "utf8");
14195
15520
  }
@@ -14238,7 +15563,7 @@ async function readSyncLocalTriggerState(statePath) {
14238
15563
  return nextState;
14239
15564
  }
14240
15565
  async function writeSyncLocalTriggerState(statePath, state) {
14241
- await mkdir5(path25.dirname(statePath), { recursive: true });
15566
+ await mkdir5(path26.dirname(statePath), { recursive: true });
14242
15567
  await writeFile4(statePath, `${JSON.stringify(state, null, 2)}
14243
15568
  `, "utf8");
14244
15569
  }
@@ -14253,12 +15578,12 @@ async function readSyncLocalLock(lockPath) {
14253
15578
  return { pid: lock.pid, startedAt: lock.startedAt };
14254
15579
  }
14255
15580
  async function writeSyncLocalLock(lockPath, lock) {
14256
- await mkdir5(path25.dirname(lockPath), { recursive: true });
15581
+ await mkdir5(path26.dirname(lockPath), { recursive: true });
14257
15582
  await writeFile4(lockPath, `${JSON.stringify(lock, null, 2)}
14258
15583
  `, "utf8");
14259
15584
  }
14260
15585
  async function acquireSyncLocalLock(lockPath, lock) {
14261
- await mkdir5(path25.dirname(lockPath), { recursive: true });
15586
+ await mkdir5(path26.dirname(lockPath), { recursive: true });
14262
15587
  try {
14263
15588
  const handle = await open(lockPath, "wx");
14264
15589
  try {
@@ -14338,10 +15663,10 @@ function syncLocalRunnerEntryArgs(cliPath) {
14338
15663
  if (cliPath.endsWith(".ts")) {
14339
15664
  return ["--import", "tsx", cliPath];
14340
15665
  }
14341
- return [path25.resolve(path25.dirname(cliPath), "../bin/vibetime.mjs")];
15666
+ return [path26.resolve(path26.dirname(cliPath), "../bin/vibetime.mjs")];
14342
15667
  }
14343
15668
  function resolveHome3(options, ctx) {
14344
- return path25.resolve(stringOption(options.home) || ctx.env.HOME || os12.homedir());
15669
+ return path26.resolve(stringOption(options.home) || ctx.env.HOME || os13.homedir());
14345
15670
  }
14346
15671
  function requestedTargets(options) {
14347
15672
  const value = options.target || options.targets;