vibeusage 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -190,7 +190,7 @@ function renderWelcome() {
|
|
|
190
190
|
DIVIDER,
|
|
191
191
|
"",
|
|
192
192
|
"This tool will:",
|
|
193
|
-
" - Analyze your local AI CLI configurations (Codex, Every Code, Claude, Gemini, Kimi,
|
|
193
|
+
" - Analyze your local AI CLI configurations (Codex, Every Code, Claude Code, Gemini, Kimi, Hermes, OpenCode, OpenClaw)",
|
|
194
194
|
" - Set up lightweight hooks to track your flow state",
|
|
195
195
|
" - Link your device to your VibeScore account",
|
|
196
196
|
"",
|
|
@@ -3,8 +3,8 @@ const { isDir } = require("./utils");
|
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
name: "opencode",
|
|
6
|
-
summaryLabel: "
|
|
7
|
-
statusLabel: "
|
|
6
|
+
summaryLabel: "OpenCode Plugin",
|
|
7
|
+
statusLabel: "OpenCode plugin",
|
|
8
8
|
async probe(ctx) {
|
|
9
9
|
const hasConfigDir = await isDir(ctx.opencode.configDir);
|
|
10
10
|
if (!hasConfigDir) {
|
package/src/lib/rollout.js
CHANGED
|
@@ -246,6 +246,8 @@ async function parseClaudeIncremental({
|
|
|
246
246
|
const prev = cursors.files[key] || null;
|
|
247
247
|
const inode = st.ino || 0;
|
|
248
248
|
const startOffset = prev && prev.inode === inode ? prev.offset || 0 : 0;
|
|
249
|
+
const priorSeenIds =
|
|
250
|
+
prev && prev.inode === inode && Array.isArray(prev.seenIds) ? prev.seenIds : [];
|
|
249
251
|
|
|
250
252
|
const projectContext = projectEnabled
|
|
251
253
|
? await resolveProjectContextForFile({
|
|
@@ -269,11 +271,13 @@ async function parseClaudeIncremental({
|
|
|
269
271
|
projectTouchedBuckets,
|
|
270
272
|
projectRef,
|
|
271
273
|
projectKey,
|
|
274
|
+
priorSeenIds,
|
|
272
275
|
});
|
|
273
276
|
|
|
274
277
|
cursors.files[key] = {
|
|
275
278
|
inode,
|
|
276
279
|
offset: result.endOffset,
|
|
280
|
+
seenIds: result.seenIds,
|
|
277
281
|
updatedAt: new Date().toISOString(),
|
|
278
282
|
};
|
|
279
283
|
|
|
@@ -778,6 +782,8 @@ async function parseRolloutFile({
|
|
|
778
782
|
return { endOffset, lastTotal: totals, lastModel: model, eventsAggregated };
|
|
779
783
|
}
|
|
780
784
|
|
|
785
|
+
const CLAUDE_SEEN_IDS_LIMIT = 500;
|
|
786
|
+
|
|
781
787
|
async function parseClaudeFile({
|
|
782
788
|
filePath,
|
|
783
789
|
startOffset,
|
|
@@ -788,12 +794,18 @@ async function parseClaudeFile({
|
|
|
788
794
|
projectTouchedBuckets,
|
|
789
795
|
projectRef,
|
|
790
796
|
projectKey,
|
|
797
|
+
priorSeenIds,
|
|
791
798
|
}) {
|
|
799
|
+
const seenOrder = Array.isArray(priorSeenIds) ? priorSeenIds.slice() : [];
|
|
800
|
+
const seenSet = new Set(seenOrder);
|
|
801
|
+
|
|
792
802
|
const st = await fs.stat(filePath).catch(() => null);
|
|
793
|
-
if (!st || !st.isFile())
|
|
803
|
+
if (!st || !st.isFile()) {
|
|
804
|
+
return { endOffset: startOffset, eventsAggregated: 0, seenIds: seenOrder };
|
|
805
|
+
}
|
|
794
806
|
|
|
795
807
|
const endOffset = st.size;
|
|
796
|
-
if (startOffset >= endOffset) return { endOffset, eventsAggregated: 0 };
|
|
808
|
+
if (startOffset >= endOffset) return { endOffset, eventsAggregated: 0, seenIds: seenOrder };
|
|
797
809
|
|
|
798
810
|
const stream = fssync.createReadStream(filePath, { encoding: "utf8", start: startOffset });
|
|
799
811
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
@@ -811,6 +823,12 @@ async function parseClaudeFile({
|
|
|
811
823
|
const usage = obj?.message?.usage || obj?.usage;
|
|
812
824
|
if (!usage || typeof usage !== "object") continue;
|
|
813
825
|
|
|
826
|
+
// Claude Code writes the same assistant message multiple times in the session log
|
|
827
|
+
// (same `message.id` / `requestId`, different outer `uuid`). Aggregate once per
|
|
828
|
+
// upstream Anthropic response to avoid multi-counting token usage.
|
|
829
|
+
const dedupeId = obj?.message?.id || obj?.requestId || null;
|
|
830
|
+
if (dedupeId && seenSet.has(dedupeId)) continue;
|
|
831
|
+
|
|
814
832
|
const model = normalizeModelInput(obj?.message?.model || obj?.model) || DEFAULT_MODEL;
|
|
815
833
|
const tokenTimestamp = typeof obj?.timestamp === "string" ? obj.timestamp : null;
|
|
816
834
|
if (!tokenTimestamp) continue;
|
|
@@ -835,12 +853,20 @@ async function parseClaudeFile({
|
|
|
835
853
|
addTotals(projectBucket.totals, delta);
|
|
836
854
|
projectTouchedBuckets.add(projectBucketKey(projectKey, source, bucketStart));
|
|
837
855
|
}
|
|
856
|
+
if (dedupeId) {
|
|
857
|
+
seenSet.add(dedupeId);
|
|
858
|
+
seenOrder.push(dedupeId);
|
|
859
|
+
}
|
|
838
860
|
eventsAggregated += 1;
|
|
839
861
|
}
|
|
840
862
|
|
|
841
863
|
rl.close();
|
|
842
864
|
stream.close?.();
|
|
843
|
-
|
|
865
|
+
const trimmedSeenIds =
|
|
866
|
+
seenOrder.length > CLAUDE_SEEN_IDS_LIMIT
|
|
867
|
+
? seenOrder.slice(seenOrder.length - CLAUDE_SEEN_IDS_LIMIT)
|
|
868
|
+
: seenOrder;
|
|
869
|
+
return { endOffset, eventsAggregated, seenIds: trimmedSeenIds };
|
|
844
870
|
}
|
|
845
871
|
|
|
846
872
|
async function parseKimiFile({
|
|
@@ -2181,7 +2207,10 @@ function normalizeOpencodeTokens(tokens) {
|
|
|
2181
2207
|
const cached = toNonNegativeInt(tokens.cache?.read);
|
|
2182
2208
|
const cacheWrite = toNonNegativeInt(tokens.cache?.write);
|
|
2183
2209
|
const inputTokens = input + cacheWrite;
|
|
2184
|
-
|
|
2210
|
+
// Include cache-read tokens in the total so OpenCode sessions do not
|
|
2211
|
+
// under-count the way Claude did before the parallel fix; cache-read is
|
|
2212
|
+
// real spend the user pays for on every turn.
|
|
2213
|
+
const total = inputTokens + cached + output + reasoning;
|
|
2185
2214
|
|
|
2186
2215
|
return {
|
|
2187
2216
|
input_tokens: inputTokens,
|
|
@@ -2304,12 +2333,19 @@ function normalizeUsage(u) {
|
|
|
2304
2333
|
function normalizeClaudeUsage(u) {
|
|
2305
2334
|
const inputTokens =
|
|
2306
2335
|
toNonNegativeInt(u?.input_tokens) + toNonNegativeInt(u?.cache_creation_input_tokens);
|
|
2336
|
+
const cachedInputTokens = toNonNegativeInt(u?.cache_read_input_tokens);
|
|
2307
2337
|
const outputTokens = toNonNegativeInt(u?.output_tokens);
|
|
2308
2338
|
const hasTotal = u && Object.prototype.hasOwnProperty.call(u, "total_tokens");
|
|
2309
|
-
|
|
2339
|
+
// Claude's Messages API does not emit `total_tokens`. When absent, compose it
|
|
2340
|
+
// from all four channels (input / cache_creation / cache_read / output). The
|
|
2341
|
+
// old formula omitted cache_read, which is ~99% of token spend on long
|
|
2342
|
+
// Claude Opus sessions and was the main driver of user-visible under-counts.
|
|
2343
|
+
const totalTokens = hasTotal
|
|
2344
|
+
? toNonNegativeInt(u?.total_tokens)
|
|
2345
|
+
: inputTokens + cachedInputTokens + outputTokens;
|
|
2310
2346
|
return {
|
|
2311
2347
|
input_tokens: inputTokens,
|
|
2312
|
-
cached_input_tokens:
|
|
2348
|
+
cached_input_tokens: cachedInputTokens,
|
|
2313
2349
|
output_tokens: outputTokens,
|
|
2314
2350
|
reasoning_output_tokens: 0,
|
|
2315
2351
|
total_tokens: totalTokens,
|