ccclub 0.2.2 → 0.2.3
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/dist/index.js +36 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -175,10 +175,12 @@ async function collectUsageEntries() {
|
|
|
175
175
|
const projectsDir = join3(homedir3(), CLAUDE_PROJECTS_DIR);
|
|
176
176
|
const files = await glob("**/*.jsonl", { cwd: projectsDir, absolute: true });
|
|
177
177
|
if (files.length === 0) {
|
|
178
|
-
return [];
|
|
178
|
+
return { entries: [], humanTurns: [] };
|
|
179
179
|
}
|
|
180
180
|
const entries = [];
|
|
181
|
+
const humanTurns = [];
|
|
181
182
|
const seen = /* @__PURE__ */ new Set();
|
|
183
|
+
const seenHuman = /* @__PURE__ */ new Set();
|
|
182
184
|
for (const file of files) {
|
|
183
185
|
const content = await readFile3(file, "utf-8");
|
|
184
186
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
@@ -189,6 +191,14 @@ async function collectUsageEntries() {
|
|
|
189
191
|
} catch {
|
|
190
192
|
continue;
|
|
191
193
|
}
|
|
194
|
+
if (parsed.type === "human" && parsed.timestamp) {
|
|
195
|
+
const humanKey = `${parsed.sessionId || ""}:${parsed.timestamp}`;
|
|
196
|
+
if (!seenHuman.has(humanKey)) {
|
|
197
|
+
seenHuman.add(humanKey);
|
|
198
|
+
humanTurns.push(parsed.timestamp);
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
192
202
|
if (parsed.type !== "assistant" || !parsed.message?.usage) {
|
|
193
203
|
continue;
|
|
194
204
|
}
|
|
@@ -217,7 +227,8 @@ async function collectUsageEntries() {
|
|
|
217
227
|
}
|
|
218
228
|
}
|
|
219
229
|
entries.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
220
|
-
|
|
230
|
+
humanTurns.sort();
|
|
231
|
+
return { entries, humanTurns };
|
|
221
232
|
}
|
|
222
233
|
|
|
223
234
|
// src/aggregator.ts
|
|
@@ -226,12 +237,26 @@ function floorToHour(date) {
|
|
|
226
237
|
floored.setUTCMinutes(0, 0, 0);
|
|
227
238
|
return floored;
|
|
228
239
|
}
|
|
229
|
-
function aggregateToBlocks(entries) {
|
|
240
|
+
function aggregateToBlocks(entries, humanTurns = []) {
|
|
230
241
|
if (entries.length === 0) return [];
|
|
242
|
+
const humanTurnMs = humanTurns.map((t) => new Date(t).getTime());
|
|
231
243
|
const blocks = [];
|
|
232
244
|
let blockStart = floorToHour(new Date(entries[0].timestamp));
|
|
233
245
|
let blockEnd = new Date(blockStart.getTime() + BLOCK_DURATION_MS);
|
|
234
246
|
let currentBlock = [];
|
|
247
|
+
let humanIdx = 0;
|
|
248
|
+
function countHumanTurns() {
|
|
249
|
+
const startMs = blockStart.getTime();
|
|
250
|
+
const endMs = blockEnd.getTime();
|
|
251
|
+
let count = 0;
|
|
252
|
+
while (humanIdx < humanTurnMs.length && humanTurnMs[humanIdx] < startMs) humanIdx++;
|
|
253
|
+
let i = humanIdx;
|
|
254
|
+
while (i < humanTurnMs.length && humanTurnMs[i] < endMs) {
|
|
255
|
+
count++;
|
|
256
|
+
i++;
|
|
257
|
+
}
|
|
258
|
+
return count;
|
|
259
|
+
}
|
|
235
260
|
function flushBlock() {
|
|
236
261
|
if (currentBlock.length === 0) return;
|
|
237
262
|
const models = /* @__PURE__ */ new Set();
|
|
@@ -270,7 +295,8 @@ function aggregateToBlocks(entries) {
|
|
|
270
295
|
totalTokens,
|
|
271
296
|
costUSD: Math.round(costUSD * 1e4) / 1e4,
|
|
272
297
|
models: Array.from(models),
|
|
273
|
-
entryCount: currentBlock.length
|
|
298
|
+
entryCount: currentBlock.length,
|
|
299
|
+
chatCount: countHumanTurns()
|
|
274
300
|
});
|
|
275
301
|
}
|
|
276
302
|
for (const entry of entries) {
|
|
@@ -297,13 +323,13 @@ async function doSync(firstSync = false, silent = false) {
|
|
|
297
323
|
} : console.log;
|
|
298
324
|
const spinner = silent ? null : ora("Collecting usage data...").start();
|
|
299
325
|
try {
|
|
300
|
-
const entries = await collectUsageEntries();
|
|
326
|
+
const { entries, humanTurns } = await collectUsageEntries();
|
|
301
327
|
if (spinner) spinner.text = `Found ${entries.length} entries`;
|
|
302
328
|
if (entries.length === 0) {
|
|
303
329
|
if (spinner) spinner.warn("No usage data found in ~/.claude/projects/");
|
|
304
330
|
return;
|
|
305
331
|
}
|
|
306
|
-
const allBlocks = aggregateToBlocks(entries);
|
|
332
|
+
const allBlocks = aggregateToBlocks(entries, humanTurns);
|
|
307
333
|
const lastSyncPath = getLastSyncPath();
|
|
308
334
|
let lastSync = null;
|
|
309
335
|
if (existsSync3(lastSyncPath)) {
|
|
@@ -559,7 +585,7 @@ function printGroup(data, code, period, config) {
|
|
|
559
585
|
console.log(chalk4.dim(` ${period.toUpperCase()} \xB7 ${data.start.slice(0, 10)} \u2192 ${data.end.slice(0, 10)} \xB7 ${data.group.memberCount} members
|
|
560
586
|
`));
|
|
561
587
|
const table = new Table({
|
|
562
|
-
head: ["#", "Name", "Tokens", "Cost", "
|
|
588
|
+
head: ["#", "Name", "Tokens", "Cost", "Chats"].map((h) => chalk4.cyan(h)),
|
|
563
589
|
style: { head: [], border: [] },
|
|
564
590
|
colWidths: [5, 20, 16, 12, 8]
|
|
565
591
|
});
|
|
@@ -573,7 +599,7 @@ function printGroup(data, code, period, config) {
|
|
|
573
599
|
name,
|
|
574
600
|
entry.totalTokens.toLocaleString(),
|
|
575
601
|
`$${entry.costUSD.toFixed(2)}`,
|
|
576
|
-
String(entry.entryCount)
|
|
602
|
+
String(entry.chatCount || entry.entryCount)
|
|
577
603
|
]);
|
|
578
604
|
}
|
|
579
605
|
console.log(table.toString());
|
|
@@ -649,8 +675,8 @@ async function showDataCommand() {
|
|
|
649
675
|
console.log(chalk6.bold("\n What CCClub uploads:\n"));
|
|
650
676
|
console.log(chalk6.dim(" Only aggregated 5-hour block summaries. No conversation content,"));
|
|
651
677
|
console.log(chalk6.dim(" no file paths, no project names, no session details.\n"));
|
|
652
|
-
const entries = await collectUsageEntries();
|
|
653
|
-
const blocks = aggregateToBlocks(entries);
|
|
678
|
+
const { entries, humanTurns } = await collectUsageEntries();
|
|
679
|
+
const blocks = aggregateToBlocks(entries, humanTurns);
|
|
654
680
|
if (blocks.length === 0) {
|
|
655
681
|
console.log(chalk6.yellow(" No usage data found in ~/.claude/projects/"));
|
|
656
682
|
return;
|