@yhong91/vibetime 0.1.60 → 0.1.62

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 +124 -107
  2. package/package.json +1 -1
package/bin/vibetime.mjs CHANGED
@@ -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.60" : "0.1.1";
2050
+ var PACKAGE_VERSION = true ? "0.1.62" : "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;
@@ -3364,9 +3364,12 @@ async function isHooksJsonInstalled(filePath, command) {
3364
3364
  if (!isPlainObject(config) || !isPlainObject(config.hooks)) {
3365
3365
  return false;
3366
3366
  }
3367
- return Object.values(config.hooks).some(
3368
- (groups) => Array.isArray(groups) && groups.some(
3369
- (group) => isPlainObject(group) && Array.isArray(group.hooks) && group.hooks.some((hook) => hook.command === command)
3367
+ const hookScopes = [config.hooks, isPlainObject(config.hooks.events) ? config.hooks.events : {}];
3368
+ return hookScopes.some(
3369
+ (scope) => Object.values(scope).some(
3370
+ (groups) => Array.isArray(groups) && groups.some(
3371
+ (group) => isPlainObject(group) && Array.isArray(group.hooks) && group.hooks.some((hook) => hook.command === command)
3372
+ )
3370
3373
  )
3371
3374
  );
3372
3375
  } catch {
@@ -3375,11 +3378,7 @@ async function isHooksJsonInstalled(filePath, command) {
3375
3378
  }
3376
3379
 
3377
3380
  // src/adapters/claude-code.ts
3378
- init_fs();
3379
3381
  async function parseClaudeCodeSessionFile(filePath, options) {
3380
- if (path7.basename(filePath) === "usage.jsonl" && path7.basename(path7.dirname(filePath)) === ".viberouter") {
3381
- return parseClaudeRouterUsageFile(filePath, options);
3382
- }
3383
3382
  const text = await readFile3(filePath, "utf8");
3384
3383
  const lines = text.split("\n").filter(Boolean);
3385
3384
  const projectContext = await claudeProjectContextFromLines(filePath, lines, options);
@@ -3646,86 +3645,6 @@ async function parseClaudeCodeSessionFile(filePath, options) {
3646
3645
  }
3647
3646
  return state.events.filter((event) => validateCanonicalEvent(event).valid);
3648
3647
  }
3649
- async function parseClaudeRouterUsageFile(filePath, options) {
3650
- const rows = (await readFile3(filePath, "utf8")).split("\n").map(parseJsonLine).filter((row) => Boolean(
3651
- row && row.surface === "claude" && (numberField(row, "timestamp") ?? 0) > 0 && Object.keys(objectField(row, "usage")).length > 0
3652
- ));
3653
- if (rows.length === 0) {
3654
- return [];
3655
- }
3656
- const timestamps = rows.map((row) => numberField(row, "timestamp") ?? 0);
3657
- const firstTs = Math.min(...timestamps) - 6e4;
3658
- const lastTs = Math.max(...timestamps) + 6e4;
3659
- const home = path7.resolve(stringOption(options.home) || path7.dirname(path7.dirname(filePath)));
3660
- const transcriptCounts = /* @__PURE__ */ new Map();
3661
- for (const transcript of await listJsonlFiles(path7.join(claudeConfigDir(home, process.env), "projects"))) {
3662
- const seen = /* @__PURE__ */ new Set();
3663
- for (const line of (await readFile3(transcript, "utf8")).split("\n")) {
3664
- const raw = parseJsonLine(line);
3665
- const ts = raw ? Date.parse(timestampFrom(raw.timestamp) || "") : Number.NaN;
3666
- if (!raw || stringField(raw, "type") !== "assistant" || ts < firstTs || ts > lastTs) {
3667
- continue;
3668
- }
3669
- const message = objectField(raw, "message");
3670
- const messageId = stringField(message, "id");
3671
- const usageKey = messageId ? `${messageId}:${stringField(raw, "requestId")}` : null;
3672
- if (usageKey && seen.has(usageKey)) {
3673
- continue;
3674
- }
3675
- if (usageKey) {
3676
- seen.add(usageKey);
3677
- }
3678
- const usage = claudeUsageFromMessage(message);
3679
- if (!usage) {
3680
- continue;
3681
- }
3682
- const key = `${usage.tokensInput || 0}:${usage.tokensOutput || 0}`;
3683
- transcriptCounts.set(key, (transcriptCounts.get(key) || 0) + 1);
3684
- }
3685
- }
3686
- const sourcePathHash = `sha256:${createStableHash(filePath)}`;
3687
- const events = [];
3688
- for (const row of rows) {
3689
- const usage = objectField(row, "usage");
3690
- const input = numberField(usage, "inputTokens") || 0;
3691
- const output = numberField(usage, "outputTokens") || 0;
3692
- const key = `${input}:${output}`;
3693
- const matched = transcriptCounts.get(key) || 0;
3694
- if (matched > 0) {
3695
- transcriptCounts.set(key, matched - 1);
3696
- continue;
3697
- }
3698
- const timestamp = numberField(row, "timestamp") ?? 0;
3699
- const requestId = stringField(row, "requestId") || createStableHash(row).slice(0, 24);
3700
- const cacheCreation = numberField(usage, "cacheCreationInputTokens") || 0;
3701
- const cacheRead = numberField(usage, "cacheReadInputTokens") || 0;
3702
- const cached = numberField(usage, "cachedInputTokens") || cacheCreation + cacheRead;
3703
- const event = baseClaudeEvent({
3704
- ts: new Date(timestamp).toISOString(),
3705
- type: "model.usage",
3706
- sessionId: `viberouter:${new Date(timestamp).toISOString().slice(0, 10)}`,
3707
- project: "unknown",
3708
- model: stringField(row, "resolvedModel") || stringField(row, "requestedModel") || stringField(row, "model"),
3709
- provider: stringField(row, "provider"),
3710
- confidence: "exact",
3711
- metrics: {
3712
- tokensInput: input || void 0,
3713
- tokensCachedInput: cached || void 0,
3714
- tokensCacheCreationInput: cacheCreation || void 0,
3715
- tokensCacheReadInput: cacheRead || void 0,
3716
- tokensOutput: output || void 0,
3717
- tokensReasoningOutput: numberField(usage, "reasoningOutputTokens") || void 0,
3718
- tokensTotal: numberField(usage, "totalTokens") || input + output || void 0,
3719
- modelCalls: 1
3720
- },
3721
- refs: stringRefs({ sourceId: requestId, sourcePathHash, importKey: `claude-code:viberouter:${requestId}` })
3722
- });
3723
- if (validateCanonicalEvent(event).valid) {
3724
- events.push(event);
3725
- }
3726
- }
3727
- return events;
3728
- }
3729
3648
  function baseClaudeEvent(event) {
3730
3649
  return {
3731
3650
  schemaVersion: AGENT_TIME_SCHEMA_VERSION,
@@ -4062,7 +3981,6 @@ function createClaudeCodeAdapter() {
4062
3981
  const base = claudeConfigDir(home, env);
4063
3982
  return [
4064
3983
  path7.join(base, "projects"),
4065
- path7.join(home, ".viberouter", "usage.jsonl"),
4066
3984
  path7.join(base, ".claude.json"),
4067
3985
  path7.join(home, ".claude.json")
4068
3986
  ];
@@ -6833,11 +6751,12 @@ async function readCursorAccessTokenFromHome(options) {
6833
6751
  return void 0;
6834
6752
  }
6835
6753
  function cloudUsageToPersisted(row, index = 0) {
6754
+ const tokensInput = (row.tokensInput || 0) + (row.tokensCacheReadInput || 0) + (row.tokensCacheCreationInput || 0);
6836
6755
  return {
6837
6756
  generationId: `${CURSOR_CLOUD_USAGE_GENERATION_ID}:${index}:${row.tokensInput}:${row.tokensOutput}:${row.tokensCacheReadInput}`,
6838
6757
  ts: row.ts,
6839
6758
  model: row.model,
6840
- tokensInput: row.tokensInput || void 0,
6759
+ tokensInput: tokensInput || void 0,
6841
6760
  tokensOutput: row.tokensOutput || void 0,
6842
6761
  tokensCacheReadInput: row.tokensCacheReadInput || void 0,
6843
6762
  tokensCacheCreationInput: row.tokensCacheCreationInput || void 0
@@ -13232,7 +13151,6 @@ import { execFile } from "node:child_process";
13232
13151
  import { readFile as readFile15, stat as stat14 } from "node:fs/promises";
13233
13152
  import path22 from "node:path";
13234
13153
  import { promisify as promisify2 } from "node:util";
13235
- init_fs();
13236
13154
  var execFileAsync = promisify2(execFile);
13237
13155
  function zcodeCliDir(home, env) {
13238
13156
  const override = env?.ZCODE_CLI_DIR || env?.ZCODE_HOME;
@@ -13244,6 +13162,27 @@ function zcodeCliDir(home, env) {
13244
13162
  function zcodeDbPath(home, env) {
13245
13163
  return path22.join(zcodeCliDir(home, env), "db", "db.sqlite");
13246
13164
  }
13165
+ function zcodeConfigPath(home, env) {
13166
+ return path22.join(zcodeCliDir(home, env), "config.json");
13167
+ }
13168
+ var zcodeHandler = (msg) => hookHandler("zcode", msg);
13169
+ function hookConfig9() {
13170
+ const anyTool = [{ matcher: ".*", hooks: [zcodeHandler("Reporting tool activity")] }];
13171
+ return {
13172
+ hooks: {
13173
+ enabled: true,
13174
+ events: {
13175
+ SessionStart: [{ hooks: [zcodeHandler("Reporting session start")] }],
13176
+ UserPromptSubmit: [{ hooks: [zcodeHandler("Reporting prompt activity")] }],
13177
+ PreToolUse: anyTool,
13178
+ PermissionRequest: anyTool,
13179
+ PostToolUse: anyTool,
13180
+ PostToolUseFailure: anyTool,
13181
+ Stop: [{ hooks: [zcodeHandler("Reporting turn completion")] }]
13182
+ }
13183
+ }
13184
+ };
13185
+ }
13247
13186
  var providerNameCache = null;
13248
13187
  async function loadProviderNames(configPath2) {
13249
13188
  let fileMtime = 0;
@@ -13325,10 +13264,14 @@ async function readZCodeRows(dbPath) {
13325
13264
  'id',id,
13326
13265
  'parent_id',parent_id,
13327
13266
  'directory',directory,
13267
+ 'workspace_id',workspace_id,
13328
13268
  'title',title,
13329
13269
  'task_type',task_type,
13330
13270
  'time_created',time_created,
13331
- 'time_updated',time_updated
13271
+ 'time_updated',time_updated,
13272
+ 'summary_additions',summary_additions,
13273
+ 'summary_deletions',summary_deletions,
13274
+ 'summary_files',summary_files
13332
13275
  ) from session order by time_created, id;`,
13333
13276
  `select json_object(
13334
13277
  'kind','turn',
@@ -13355,6 +13298,7 @@ async function readZCodeRows(dbPath) {
13355
13298
  'query_source',query_source,
13356
13299
  'provider_id',provider_id,
13357
13300
  'model_id',model_id,
13301
+ 'variant',variant,
13358
13302
  'agent',agent,
13359
13303
  'mode',mode,
13360
13304
  'status',status,
@@ -13368,7 +13312,9 @@ async function readZCodeRows(dbPath) {
13368
13312
  'cache_read_input_tokens',cache_read_input_tokens,
13369
13313
  'provider_total_tokens',provider_total_tokens,
13370
13314
  'computed_total_tokens',computed_total_tokens,
13371
- 'tool_call_count',tool_call_count
13315
+ 'tool_call_count',tool_call_count,
13316
+ 'retry_count',retry_count,
13317
+ 'error_type',error_type
13372
13318
  ) from model_usage order by started_at, id;`,
13373
13319
  `select json_object(
13374
13320
  'kind','tool',
@@ -13386,7 +13332,8 @@ async function readZCodeRows(dbPath) {
13386
13332
  'completed_at',completed_at,
13387
13333
  'duration_ms',duration_ms,
13388
13334
  'output_bytes',output_bytes,
13389
- 'exit_code',exit_code
13335
+ 'exit_code',exit_code,
13336
+ 'error_type',error_type
13390
13337
  ) from tool_usage order by started_at, id;`
13391
13338
  ];
13392
13339
  const results = await Promise.all(queries.map((query) => sqliteJsonRows(dbPath, query)));
@@ -13408,6 +13355,7 @@ function modelMetrics(row) {
13408
13355
  const alignedInput = input > 0 ? input + (cacheIsSeparate ? cached : 0) : cached;
13409
13356
  const alignedTotal = alignedInput + output;
13410
13357
  const durationMs = numberField(row, "duration_ms");
13358
+ const variant = stringField(row, "variant");
13411
13359
  return {
13412
13360
  tokensInput: alignedInput || void 0,
13413
13361
  tokensOutput: output,
@@ -13416,6 +13364,7 @@ function modelMetrics(row) {
13416
13364
  tokensCacheCreationInput: cacheCreation,
13417
13365
  tokensCacheReadInput: cacheRead,
13418
13366
  tokensTotal: alignedTotal,
13367
+ reasoningEffort: variant || void 0,
13419
13368
  modelDurationMs: durationMs,
13420
13369
  durationMs,
13421
13370
  modelCalls: 1,
@@ -13445,13 +13394,14 @@ function sessionContext(row, sessions) {
13445
13394
  const cwd = stringField(session, "directory");
13446
13395
  const project = projectFromDirectory(cwd);
13447
13396
  const rootSessionId = resolveRootSessionId2(sessionId, sessions);
13397
+ const workspaceId = stringField(session, "workspace_id") || createWorkspaceId({ projectName: project, repoRoot: cwd });
13448
13398
  return {
13449
13399
  rawSessionId: sessionId,
13450
13400
  sessionId: `zcode:${rootSessionId}`,
13451
13401
  isSubagent: rootSessionId !== sessionId,
13452
13402
  cwd,
13453
13403
  project,
13454
- workspaceId: createWorkspaceId({ projectName: project, repoRoot: cwd }),
13404
+ workspaceId,
13455
13405
  parentSessionId: stringField(session, "parent_id") ? `zcode:${stringField(session, "parent_id")}` : void 0
13456
13406
  };
13457
13407
  }
@@ -13515,6 +13465,9 @@ async function parseZCodeDb(filePath, options) {
13515
13465
  if (event) events.push(event);
13516
13466
  const completedTs = isoFromMs(numberField(row, "time_updated"));
13517
13467
  if (completedTs && completedTs !== ts) {
13468
+ const linesAdded = numberField(row, "summary_additions");
13469
+ const linesRemoved = numberField(row, "summary_deletions");
13470
+ const filesChanged = numberField(row, "summary_files");
13518
13471
  const completed = makeEvent2({
13519
13472
  schemaVersion: AGENT_TIME_SCHEMA_VERSION,
13520
13473
  ts: completedTs,
@@ -13527,6 +13480,7 @@ async function parseZCodeDb(filePath, options) {
13527
13480
  parentAgentInstanceId: ctx.parentSessionId,
13528
13481
  agent: "zcode",
13529
13482
  confidence: "exact",
13483
+ metrics: linesAdded || linesRemoved || filesChanged ? { linesAdded: linesAdded || void 0, linesRemoved: linesRemoved || void 0, filesChanged: filesChanged || void 0 } : void 0,
13530
13484
  refs: { sourceId: `${id}:session:ended` }
13531
13485
  }, row, rowNumber, filePath, options);
13532
13486
  if (completed) events.push(completed);
@@ -13615,7 +13569,10 @@ async function parseZCodeDb(filePath, options) {
13615
13569
  sourceId: stringField(row, "id") || `${ctx.rawSessionId}:model:${rowNumber}`,
13616
13570
  zcodeQuerySource: stringField(row, "query_source"),
13617
13571
  zcodeMode: stringField(row, "mode"),
13618
- zcodeStatus: stringField(row, "status")
13572
+ zcodeStatus: stringField(row, "status"),
13573
+ zcodeVariant: stringField(row, "variant"),
13574
+ zcodeErrorType: stringField(row, "error_type"),
13575
+ zcodeRetryCount: numberField(row, "retry_count") || void 0
13619
13576
  })
13620
13577
  }, row, rowNumber, filePath, options);
13621
13578
  if (usage) events.push(usage);
@@ -13678,7 +13635,8 @@ async function parseZCodeDb(filePath, options) {
13678
13635
  refs: refs({
13679
13636
  sourceId: `${toolCallId}:completed`,
13680
13637
  zcodeStatus: status,
13681
- zcodeExitCode: numberField(row, "exit_code")
13638
+ zcodeExitCode: numberField(row, "exit_code"),
13639
+ zcodeErrorType: stringField(row, "error_type")
13682
13640
  })
13683
13641
  }, row, rowNumber, filePath, options);
13684
13642
  if (completed) events.push(completed);
@@ -13692,7 +13650,15 @@ async function zcodeBackfillFiles(sourceRoot, home, env) {
13692
13650
  const filePath = candidate.endsWith(".sqlite") ? candidate : path22.join(candidate, "db", "db.sqlite");
13693
13651
  try {
13694
13652
  const info = await stat14(filePath);
13695
- return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
13653
+ let modifiedMs = info.mtimeMs;
13654
+ for (const suffix of ["-wal", "-shm"]) {
13655
+ try {
13656
+ const sidecar = await stat14(`${filePath}${suffix}`);
13657
+ modifiedMs = Math.max(modifiedMs, sidecar.mtimeMs);
13658
+ } catch {
13659
+ }
13660
+ }
13661
+ return [{ path: filePath, modifiedAt: new Date(modifiedMs).toISOString() }];
13696
13662
  } catch {
13697
13663
  return [];
13698
13664
  }
@@ -13707,13 +13673,20 @@ function createZCodeAdapter() {
13707
13673
  return zcodeCliDir(home, env);
13708
13674
  },
13709
13675
  installedPath(home, env) {
13710
- return zcodeDbPath(home, env);
13676
+ return zcodeConfigPath(home, env);
13711
13677
  },
13712
13678
  async isInstalled(home, env) {
13713
- return pathExists(zcodeDbPath(home, env));
13679
+ return isHooksJsonInstalled(
13680
+ zcodeConfigPath(home, env),
13681
+ "vibetime hook --agent zcode"
13682
+ );
13714
13683
  },
13715
- installEntries() {
13716
- return [];
13684
+ installEntries(home, env) {
13685
+ return [{
13686
+ kind: "hooks-json",
13687
+ path: zcodeConfigPath(home, env),
13688
+ content: hookConfig9()
13689
+ }];
13717
13690
  },
13718
13691
  sourcePaths(home, env) {
13719
13692
  return [zcodeDbPath(home, env)];
@@ -14639,11 +14612,24 @@ function mergeHookObjects(existing, addition) {
14639
14612
  const additionHooks = isPlainObject(additionObject.hooks) ? additionObject.hooks : {};
14640
14613
  merged.hooks = isPlainObject(merged.hooks) ? merged.hooks : {};
14641
14614
  const mergedHooks = merged.hooks;
14642
- for (const [event, groups] of Object.entries(additionHooks)) {
14615
+ mergeHookEvents(mergedHooks, additionHooks);
14616
+ const additionEvents = isPlainObject(additionHooks.events) ? additionHooks.events : {};
14617
+ if (Object.keys(additionEvents).length > 0) {
14618
+ const mergedEvents = isPlainObject(mergedHooks.events) ? mergedHooks.events : {};
14619
+ mergeHookEvents(mergedEvents, additionEvents);
14620
+ mergedHooks.events = mergedEvents;
14621
+ if (additionHooks.enabled === true) {
14622
+ mergedHooks.enabled = true;
14623
+ }
14624
+ }
14625
+ return merged;
14626
+ }
14627
+ function mergeHookEvents(target, addition) {
14628
+ for (const [event, groups] of Object.entries(addition)) {
14643
14629
  if (!Array.isArray(groups)) {
14644
14630
  continue;
14645
14631
  }
14646
- const existingGroups = Array.isArray(mergedHooks[event]) ? mergedHooks[event] : [];
14632
+ const existingGroups = Array.isArray(target[event]) ? target[event] : [];
14647
14633
  const nextGroups = [...existingGroups];
14648
14634
  for (const group of groups) {
14649
14635
  const command = hookCommandFromGroup(group);
@@ -14654,9 +14640,8 @@ function mergeHookObjects(existing, addition) {
14654
14640
  nextGroups.push(group);
14655
14641
  }
14656
14642
  }
14657
- mergedHooks[event] = nextGroups;
14643
+ target[event] = nextGroups;
14658
14644
  }
14659
- return merged;
14660
14645
  }
14661
14646
  function hookCommandFromGroup(group) {
14662
14647
  if (!isPlainObject(group) || !Array.isArray(group.hooks)) {
@@ -14745,6 +14730,12 @@ function collectHookCommandsFromJsonContent(content) {
14745
14730
  for (const groups of Object.values(content.hooks)) {
14746
14731
  walkGroups(groups);
14747
14732
  }
14733
+ const nestedEvents = content.hooks.events;
14734
+ if (isPlainObject(nestedEvents)) {
14735
+ for (const groups of Object.values(nestedEvents)) {
14736
+ walkGroups(groups);
14737
+ }
14738
+ }
14748
14739
  }
14749
14740
  for (const [key, value] of Object.entries(content)) {
14750
14741
  if (key === "hooks" || key === "enable_json_hooks" || !isPlainObject(value)) {
@@ -14809,7 +14800,33 @@ async function uninstallHooksJson(filePath, content, { dryRun, onWrite }) {
14809
14800
  }
14810
14801
  }
14811
14802
  }
14812
- if (isPlainObject(next.hooks) && Object.keys(next.hooks).length === 0) {
14803
+ if (isPlainObject(next.hooks.events)) {
14804
+ for (const [event, groups] of Object.entries(next.hooks.events)) {
14805
+ const stripped = stripHookCommandsFromGroups(groups, commands);
14806
+ if (JSON.stringify(stripped) !== JSON.stringify(groups)) {
14807
+ changed = true;
14808
+ if (Array.isArray(stripped) && stripped.length === 0) {
14809
+ delete next.hooks.events[event];
14810
+ } else {
14811
+ next.hooks.events[event] = stripped;
14812
+ }
14813
+ }
14814
+ }
14815
+ if (Object.keys(next.hooks.events).length === 0) {
14816
+ delete next.hooks.events;
14817
+ }
14818
+ }
14819
+ const eventsObject = isPlainObject(next.hooks.events) ? next.hooks.events : {};
14820
+ const hooksHasGroups = Object.values(next.hooks).some((value) => Array.isArray(value) && value.length > 0) || Object.values(eventsObject).some((value) => Array.isArray(value) && value.length > 0);
14821
+ if (!hooksHasGroups) {
14822
+ if (next.hooks.enabled !== void 0) {
14823
+ delete next.hooks.enabled;
14824
+ }
14825
+ if (Object.keys(next.hooks).length === 0) {
14826
+ delete next.hooks;
14827
+ changed = true;
14828
+ }
14829
+ } else if (isPlainObject(next.hooks) && Object.keys(next.hooks).length === 0) {
14813
14830
  delete next.hooks;
14814
14831
  changed = true;
14815
14832
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.60",
4
+ "version": "0.1.62",
5
5
  "description": "vibetime CLI — install AI-agent hooks (Claude Code, Codex, OpenCode, Pi, Cursor) and report activity to vibetime.",
6
6
  "license": "MIT",
7
7
  "publishConfig": {