@yhong91/vibetime 0.1.61 → 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 +122 -106
  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.61" : "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
  ];
@@ -13233,7 +13151,6 @@ import { execFile } from "node:child_process";
13233
13151
  import { readFile as readFile15, stat as stat14 } from "node:fs/promises";
13234
13152
  import path22 from "node:path";
13235
13153
  import { promisify as promisify2 } from "node:util";
13236
- init_fs();
13237
13154
  var execFileAsync = promisify2(execFile);
13238
13155
  function zcodeCliDir(home, env) {
13239
13156
  const override = env?.ZCODE_CLI_DIR || env?.ZCODE_HOME;
@@ -13245,6 +13162,27 @@ function zcodeCliDir(home, env) {
13245
13162
  function zcodeDbPath(home, env) {
13246
13163
  return path22.join(zcodeCliDir(home, env), "db", "db.sqlite");
13247
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
+ }
13248
13186
  var providerNameCache = null;
13249
13187
  async function loadProviderNames(configPath2) {
13250
13188
  let fileMtime = 0;
@@ -13326,10 +13264,14 @@ async function readZCodeRows(dbPath) {
13326
13264
  'id',id,
13327
13265
  'parent_id',parent_id,
13328
13266
  'directory',directory,
13267
+ 'workspace_id',workspace_id,
13329
13268
  'title',title,
13330
13269
  'task_type',task_type,
13331
13270
  'time_created',time_created,
13332
- 'time_updated',time_updated
13271
+ 'time_updated',time_updated,
13272
+ 'summary_additions',summary_additions,
13273
+ 'summary_deletions',summary_deletions,
13274
+ 'summary_files',summary_files
13333
13275
  ) from session order by time_created, id;`,
13334
13276
  `select json_object(
13335
13277
  'kind','turn',
@@ -13356,6 +13298,7 @@ async function readZCodeRows(dbPath) {
13356
13298
  'query_source',query_source,
13357
13299
  'provider_id',provider_id,
13358
13300
  'model_id',model_id,
13301
+ 'variant',variant,
13359
13302
  'agent',agent,
13360
13303
  'mode',mode,
13361
13304
  'status',status,
@@ -13369,7 +13312,9 @@ async function readZCodeRows(dbPath) {
13369
13312
  'cache_read_input_tokens',cache_read_input_tokens,
13370
13313
  'provider_total_tokens',provider_total_tokens,
13371
13314
  'computed_total_tokens',computed_total_tokens,
13372
- 'tool_call_count',tool_call_count
13315
+ 'tool_call_count',tool_call_count,
13316
+ 'retry_count',retry_count,
13317
+ 'error_type',error_type
13373
13318
  ) from model_usage order by started_at, id;`,
13374
13319
  `select json_object(
13375
13320
  'kind','tool',
@@ -13387,7 +13332,8 @@ async function readZCodeRows(dbPath) {
13387
13332
  'completed_at',completed_at,
13388
13333
  'duration_ms',duration_ms,
13389
13334
  'output_bytes',output_bytes,
13390
- 'exit_code',exit_code
13335
+ 'exit_code',exit_code,
13336
+ 'error_type',error_type
13391
13337
  ) from tool_usage order by started_at, id;`
13392
13338
  ];
13393
13339
  const results = await Promise.all(queries.map((query) => sqliteJsonRows(dbPath, query)));
@@ -13409,6 +13355,7 @@ function modelMetrics(row) {
13409
13355
  const alignedInput = input > 0 ? input + (cacheIsSeparate ? cached : 0) : cached;
13410
13356
  const alignedTotal = alignedInput + output;
13411
13357
  const durationMs = numberField(row, "duration_ms");
13358
+ const variant = stringField(row, "variant");
13412
13359
  return {
13413
13360
  tokensInput: alignedInput || void 0,
13414
13361
  tokensOutput: output,
@@ -13417,6 +13364,7 @@ function modelMetrics(row) {
13417
13364
  tokensCacheCreationInput: cacheCreation,
13418
13365
  tokensCacheReadInput: cacheRead,
13419
13366
  tokensTotal: alignedTotal,
13367
+ reasoningEffort: variant || void 0,
13420
13368
  modelDurationMs: durationMs,
13421
13369
  durationMs,
13422
13370
  modelCalls: 1,
@@ -13446,13 +13394,14 @@ function sessionContext(row, sessions) {
13446
13394
  const cwd = stringField(session, "directory");
13447
13395
  const project = projectFromDirectory(cwd);
13448
13396
  const rootSessionId = resolveRootSessionId2(sessionId, sessions);
13397
+ const workspaceId = stringField(session, "workspace_id") || createWorkspaceId({ projectName: project, repoRoot: cwd });
13449
13398
  return {
13450
13399
  rawSessionId: sessionId,
13451
13400
  sessionId: `zcode:${rootSessionId}`,
13452
13401
  isSubagent: rootSessionId !== sessionId,
13453
13402
  cwd,
13454
13403
  project,
13455
- workspaceId: createWorkspaceId({ projectName: project, repoRoot: cwd }),
13404
+ workspaceId,
13456
13405
  parentSessionId: stringField(session, "parent_id") ? `zcode:${stringField(session, "parent_id")}` : void 0
13457
13406
  };
13458
13407
  }
@@ -13516,6 +13465,9 @@ async function parseZCodeDb(filePath, options) {
13516
13465
  if (event) events.push(event);
13517
13466
  const completedTs = isoFromMs(numberField(row, "time_updated"));
13518
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");
13519
13471
  const completed = makeEvent2({
13520
13472
  schemaVersion: AGENT_TIME_SCHEMA_VERSION,
13521
13473
  ts: completedTs,
@@ -13528,6 +13480,7 @@ async function parseZCodeDb(filePath, options) {
13528
13480
  parentAgentInstanceId: ctx.parentSessionId,
13529
13481
  agent: "zcode",
13530
13482
  confidence: "exact",
13483
+ metrics: linesAdded || linesRemoved || filesChanged ? { linesAdded: linesAdded || void 0, linesRemoved: linesRemoved || void 0, filesChanged: filesChanged || void 0 } : void 0,
13531
13484
  refs: { sourceId: `${id}:session:ended` }
13532
13485
  }, row, rowNumber, filePath, options);
13533
13486
  if (completed) events.push(completed);
@@ -13616,7 +13569,10 @@ async function parseZCodeDb(filePath, options) {
13616
13569
  sourceId: stringField(row, "id") || `${ctx.rawSessionId}:model:${rowNumber}`,
13617
13570
  zcodeQuerySource: stringField(row, "query_source"),
13618
13571
  zcodeMode: stringField(row, "mode"),
13619
- 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
13620
13576
  })
13621
13577
  }, row, rowNumber, filePath, options);
13622
13578
  if (usage) events.push(usage);
@@ -13679,7 +13635,8 @@ async function parseZCodeDb(filePath, options) {
13679
13635
  refs: refs({
13680
13636
  sourceId: `${toolCallId}:completed`,
13681
13637
  zcodeStatus: status,
13682
- zcodeExitCode: numberField(row, "exit_code")
13638
+ zcodeExitCode: numberField(row, "exit_code"),
13639
+ zcodeErrorType: stringField(row, "error_type")
13683
13640
  })
13684
13641
  }, row, rowNumber, filePath, options);
13685
13642
  if (completed) events.push(completed);
@@ -13693,7 +13650,15 @@ async function zcodeBackfillFiles(sourceRoot, home, env) {
13693
13650
  const filePath = candidate.endsWith(".sqlite") ? candidate : path22.join(candidate, "db", "db.sqlite");
13694
13651
  try {
13695
13652
  const info = await stat14(filePath);
13696
- 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() }];
13697
13662
  } catch {
13698
13663
  return [];
13699
13664
  }
@@ -13708,13 +13673,20 @@ function createZCodeAdapter() {
13708
13673
  return zcodeCliDir(home, env);
13709
13674
  },
13710
13675
  installedPath(home, env) {
13711
- return zcodeDbPath(home, env);
13676
+ return zcodeConfigPath(home, env);
13712
13677
  },
13713
13678
  async isInstalled(home, env) {
13714
- return pathExists(zcodeDbPath(home, env));
13679
+ return isHooksJsonInstalled(
13680
+ zcodeConfigPath(home, env),
13681
+ "vibetime hook --agent zcode"
13682
+ );
13715
13683
  },
13716
- installEntries() {
13717
- return [];
13684
+ installEntries(home, env) {
13685
+ return [{
13686
+ kind: "hooks-json",
13687
+ path: zcodeConfigPath(home, env),
13688
+ content: hookConfig9()
13689
+ }];
13718
13690
  },
13719
13691
  sourcePaths(home, env) {
13720
13692
  return [zcodeDbPath(home, env)];
@@ -14640,11 +14612,24 @@ function mergeHookObjects(existing, addition) {
14640
14612
  const additionHooks = isPlainObject(additionObject.hooks) ? additionObject.hooks : {};
14641
14613
  merged.hooks = isPlainObject(merged.hooks) ? merged.hooks : {};
14642
14614
  const mergedHooks = merged.hooks;
14643
- 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)) {
14644
14629
  if (!Array.isArray(groups)) {
14645
14630
  continue;
14646
14631
  }
14647
- const existingGroups = Array.isArray(mergedHooks[event]) ? mergedHooks[event] : [];
14632
+ const existingGroups = Array.isArray(target[event]) ? target[event] : [];
14648
14633
  const nextGroups = [...existingGroups];
14649
14634
  for (const group of groups) {
14650
14635
  const command = hookCommandFromGroup(group);
@@ -14655,9 +14640,8 @@ function mergeHookObjects(existing, addition) {
14655
14640
  nextGroups.push(group);
14656
14641
  }
14657
14642
  }
14658
- mergedHooks[event] = nextGroups;
14643
+ target[event] = nextGroups;
14659
14644
  }
14660
- return merged;
14661
14645
  }
14662
14646
  function hookCommandFromGroup(group) {
14663
14647
  if (!isPlainObject(group) || !Array.isArray(group.hooks)) {
@@ -14746,6 +14730,12 @@ function collectHookCommandsFromJsonContent(content) {
14746
14730
  for (const groups of Object.values(content.hooks)) {
14747
14731
  walkGroups(groups);
14748
14732
  }
14733
+ const nestedEvents = content.hooks.events;
14734
+ if (isPlainObject(nestedEvents)) {
14735
+ for (const groups of Object.values(nestedEvents)) {
14736
+ walkGroups(groups);
14737
+ }
14738
+ }
14749
14739
  }
14750
14740
  for (const [key, value] of Object.entries(content)) {
14751
14741
  if (key === "hooks" || key === "enable_json_hooks" || !isPlainObject(value)) {
@@ -14810,7 +14800,33 @@ async function uninstallHooksJson(filePath, content, { dryRun, onWrite }) {
14810
14800
  }
14811
14801
  }
14812
14802
  }
14813
- 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) {
14814
14830
  delete next.hooks;
14815
14831
  changed = true;
14816
14832
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.61",
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": {