@yhong91/vibetime 0.1.20 → 0.1.23

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 +61 -30
  2. package/package.json +1 -1
package/bin/vibetime.mjs CHANGED
@@ -1202,7 +1202,7 @@ function countTextLines(text) {
1202
1202
  }
1203
1203
 
1204
1204
  // src/lib/constants.ts
1205
- var PACKAGE_VERSION = true ? "0.1.20" : "0.1.1";
1205
+ var PACKAGE_VERSION = true ? "0.1.23" : "0.1.1";
1206
1206
  var DEFAULT_API_URL = "http://121.196.224.82:3001";
1207
1207
  var DEFAULT_BACKFILL_BATCH_SIZE = 50;
1208
1208
  var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
@@ -2695,6 +2695,9 @@ async function parseClaudeCodeSessionFile(filePath, options) {
2695
2695
  continue;
2696
2696
  }
2697
2697
  if (topType === "user") {
2698
+ if (isClaudeNoiseUserEntry(raw, message)) {
2699
+ continue;
2700
+ }
2698
2701
  const prompt = claudeTextStats(message.content);
2699
2702
  state.closeTurn(ts, lineNumber, topType);
2700
2703
  state.currentTurnId = stringField(raw, "uuid") || stringField(raw, "promptId") || `turn_${createStableHash([sessionId, lineNumber]).slice(0, 24)}`;
@@ -2978,6 +2981,23 @@ function claudeTextStats(value) {
2978
2981
  hash: textItems.length > 0 ? `sha256:${createStableHash(textItems)}` : void 0
2979
2982
  };
2980
2983
  }
2984
+ function isClaudeNoiseUserEntry(raw, message) {
2985
+ if (raw.isMeta === true) {
2986
+ return true;
2987
+ }
2988
+ const content = message.content;
2989
+ let text = "";
2990
+ if (typeof content === "string") {
2991
+ text = content;
2992
+ } else if (Array.isArray(content)) {
2993
+ text = content.filter((item) => isPlainObject(item) && item.type === "text").map((item) => stringField(item, "text") || "").join("");
2994
+ }
2995
+ text = text.trim();
2996
+ if (!text) {
2997
+ return false;
2998
+ }
2999
+ return /^<(?:command-name|command-message|command-args|local-command-stdout|local-command-stderr|local-command-caveat)>/.test(text) || text === "[Request interrupted by user]";
3000
+ }
2981
3001
  async function claudeProjectContextFromLines(filePath, lines, options) {
2982
3002
  const projectDir = path7.basename(path7.dirname(filePath));
2983
3003
  const cwds = [];
@@ -4711,6 +4731,8 @@ async function parseCopilotSessionFile(filePath, options) {
4711
4731
  let reasoningEffort;
4712
4732
  const pendingTools = /* @__PURE__ */ new Map();
4713
4733
  const turnTokenAccum = /* @__PURE__ */ new Map();
4734
+ const pendingTurnUsage = [];
4735
+ let emittedShutdownUsage = false;
4714
4736
  for (const [index, line] of lines.entries()) {
4715
4737
  const lineNumber = index + 1;
4716
4738
  const raw = parseJsonLine(line);
@@ -4875,7 +4897,7 @@ async function parseCopilotSessionFile(filePath, options) {
4875
4897
  const accum = turnId ? turnTokenAccum.get(turnId) : void 0;
4876
4898
  if (accum && accum.tokensOutput && accum.tokensOutput > 0) {
4877
4899
  accum.reasoningEffort = reasoningEffort;
4878
- events.push(baseCopilotEvent({
4900
+ pendingTurnUsage.push(baseCopilotEvent({
4879
4901
  ts,
4880
4902
  type: "model.usage",
4881
4903
  sessionId,
@@ -4939,6 +4961,7 @@ async function parseCopilotSessionFile(filePath, options) {
4939
4961
  modelCalls: requestCount || void 0
4940
4962
  }
4941
4963
  }));
4964
+ emittedShutdownUsage = true;
4942
4965
  }
4943
4966
  events.push(baseCopilotEvent({
4944
4967
  ts,
@@ -4953,6 +4976,9 @@ async function parseCopilotSessionFile(filePath, options) {
4953
4976
  }
4954
4977
  }
4955
4978
  }
4979
+ if (!emittedShutdownUsage) {
4980
+ events.push(...pendingTurnUsage);
4981
+ }
4956
4982
  return events.filter((event) => validateCanonicalEvent(event).valid);
4957
4983
  }
4958
4984
  function baseCopilotEvent(event) {
@@ -5179,33 +5205,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
5179
5205
  const createdTs = timeCreated;
5180
5206
  const tokens = opencodeUsageFromInfo(info);
5181
5207
  const cost = typeof info.cost === "number" && info.cost > 0 ? info.cost : void 0;
5182
- if (tokens) {
5183
- const metrics = {
5184
- tokensInput: tokens.tokensInput,
5185
- tokensOutput: tokens.tokensOutput,
5186
- tokensReasoningOutput: tokens.tokensReasoningOutput,
5187
- tokensCachedInput: tokens.tokensCachedInput,
5188
- tokensCacheReadInput: tokens.tokensCacheReadInput,
5189
- tokensCacheCreationInput: tokens.tokensCacheCreationInput,
5190
- tokensTotal: tokens.tokensTotal,
5191
- reasoningEffort
5192
- };
5193
- if (cost !== void 0) {
5194
- metrics.costUsd = cost;
5195
- }
5196
- events.push(baseOpenCodeEvent({
5197
- ts: msToIso(completedTs || createdTs),
5198
- type: "model.usage",
5199
- sessionId,
5200
- turnId: currentTurnId,
5201
- cwd: assistantCwd,
5202
- project: assistantProject,
5203
- model,
5204
- provider,
5205
- operation: "model usage",
5206
- metrics
5207
- }, assistantAgent));
5208
- }
5208
+ let emittedStepUsage = false;
5209
5209
  try {
5210
5210
  const parts = db.prepare(
5211
5211
  "SELECT data FROM part WHERE message_id = ? ORDER BY id"
@@ -5306,6 +5306,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
5306
5306
  const stepTokens = opencodeUsageFromInfo(pd);
5307
5307
  const stepCost = typeof pd.cost === "number" && pd.cost > 0 ? pd.cost : void 0;
5308
5308
  if (stepTokens) {
5309
+ emittedStepUsage = true;
5309
5310
  const stepMetrics = {
5310
5311
  tokensInput: stepTokens.tokensInput,
5311
5312
  tokensOutput: stepTokens.tokensOutput,
@@ -5350,6 +5351,33 @@ async function parseOpenCodeSessionFile(dbPath, options) {
5350
5351
  }
5351
5352
  } catch {
5352
5353
  }
5354
+ if (!emittedStepUsage && tokens) {
5355
+ const metrics = {
5356
+ tokensInput: tokens.tokensInput,
5357
+ tokensOutput: tokens.tokensOutput,
5358
+ tokensReasoningOutput: tokens.tokensReasoningOutput,
5359
+ tokensCachedInput: tokens.tokensCachedInput,
5360
+ tokensCacheReadInput: tokens.tokensCacheReadInput,
5361
+ tokensCacheCreationInput: tokens.tokensCacheCreationInput,
5362
+ tokensTotal: tokens.tokensTotal,
5363
+ reasoningEffort
5364
+ };
5365
+ if (cost !== void 0) {
5366
+ metrics.costUsd = cost;
5367
+ }
5368
+ events.push(baseOpenCodeEvent({
5369
+ ts: msToIso(completedTs || createdTs),
5370
+ type: "model.usage",
5371
+ sessionId,
5372
+ turnId: currentTurnId,
5373
+ cwd: assistantCwd,
5374
+ project: assistantProject,
5375
+ model,
5376
+ provider,
5377
+ operation: "model usage",
5378
+ metrics
5379
+ }, assistantAgent));
5380
+ }
5353
5381
  if (stringField(info, "finish")) {
5354
5382
  const durationMs = completedTs && createdTs ? completedTs - createdTs : void 0;
5355
5383
  events.push(baseOpenCodeEvent({
@@ -8349,7 +8377,10 @@ function buildSessionRollups(events) {
8349
8377
  grouped.set(key, [event]);
8350
8378
  }
8351
8379
  }
8352
- return [...grouped.entries()].map(([rollupKey, sessionEvents]) => buildSessionRollup(rollupKey, sessionEvents)).sort((a, b) => a.startedAt.localeCompare(b.startedAt));
8380
+ return [...grouped.entries()].map(([rollupKey, sessionEvents]) => buildSessionRollup(rollupKey, sessionEvents)).filter((rollup) => !isPhantomRollup(rollup)).sort((a, b) => a.startedAt.localeCompare(b.startedAt));
8381
+ }
8382
+ function isPhantomRollup(rollup) {
8383
+ return rollup.totalTokens === 0 && rollup.toolCallCount === 0 && rollup.commandCallCount === 0 && rollup.linesAdded === 0 && rollup.linesRemoved === 0;
8353
8384
  }
8354
8385
  function buildSessionRollup(rollupKey, events) {
8355
8386
  const ordered = [...events].sort((a, b) => a.ts.localeCompare(b.ts));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.20",
4
+ "version": "0.1.23",
5
5
  "description": "vibetime CLI — install AI-agent hooks (Claude Code, Codex, OpenCode, Pi) and report activity to vibetime.",
6
6
  "license": "MIT",
7
7
  "publishConfig": {