claude-code-wrapped 0.1.6 โ 0.1.7
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.
|
@@ -87,6 +87,7 @@ Examples:
|
|
|
87
87
|
"most_active_day_messages": stats.most_active_day[1] if stats.most_active_day else None,
|
|
88
88
|
"primary_model": stats.primary_model,
|
|
89
89
|
"top_tools": dict(stats.top_tools),
|
|
90
|
+
"top_mcps": dict(stats.top_mcps),
|
|
90
91
|
"top_projects": dict(stats.top_projects),
|
|
91
92
|
"hourly_distribution": stats.hourly_distribution,
|
|
92
93
|
"weekday_distribution": stats.weekday_distribution,
|
|
@@ -52,6 +52,10 @@ class WrappedStats:
|
|
|
52
52
|
tool_calls: Counter = field(default_factory=Counter)
|
|
53
53
|
top_tools: list[tuple[str, int]] = field(default_factory=list)
|
|
54
54
|
|
|
55
|
+
# MCP server usage (extracted from mcp__server__tool format)
|
|
56
|
+
mcp_servers: Counter = field(default_factory=Counter)
|
|
57
|
+
top_mcps: list[tuple[str, int]] = field(default_factory=list)
|
|
58
|
+
|
|
55
59
|
# Model usage
|
|
56
60
|
models_used: Counter = field(default_factory=Counter)
|
|
57
61
|
primary_model: str | None = None
|
|
@@ -279,9 +283,16 @@ def aggregate_stats(messages: list[Message], year: int) -> WrappedStats:
|
|
|
279
283
|
if msg.session_id:
|
|
280
284
|
session_tokens[msg.session_id] += msg.usage.total_tokens
|
|
281
285
|
|
|
282
|
-
# Tool usage
|
|
286
|
+
# Tool usage (separate MCPs from regular tools)
|
|
283
287
|
for tool in msg.tool_calls:
|
|
284
|
-
|
|
288
|
+
if tool.startswith("mcp__"):
|
|
289
|
+
# Extract MCP server name: mcp__servername__toolname -> servername
|
|
290
|
+
parts = tool.split("__")
|
|
291
|
+
if len(parts) >= 2:
|
|
292
|
+
mcp_server = parts[1]
|
|
293
|
+
stats.mcp_servers[mcp_server] += 1
|
|
294
|
+
else:
|
|
295
|
+
stats.tool_calls[tool] += 1
|
|
285
296
|
|
|
286
297
|
# Time-based stats
|
|
287
298
|
if msg.timestamp:
|
|
@@ -331,6 +342,9 @@ def aggregate_stats(messages: list[Message], year: int) -> WrappedStats:
|
|
|
331
342
|
# Top tools
|
|
332
343
|
stats.top_tools = stats.tool_calls.most_common(10)
|
|
333
344
|
|
|
345
|
+
# Top MCPs
|
|
346
|
+
stats.top_mcps = stats.mcp_servers.most_common(5)
|
|
347
|
+
|
|
334
348
|
# Top projects
|
|
335
349
|
stats.top_projects = projects.most_common(5)
|
|
336
350
|
|
|
@@ -52,7 +52,7 @@ def wait_for_keypress():
|
|
|
52
52
|
return '\n'
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
def create_dramatic_stat(value: str, label: str, subtitle: str = "", color: str = COLORS["orange"]) -> Text:
|
|
55
|
+
def create_dramatic_stat(value: str, label: str, subtitle: str = "", color: str = COLORS["orange"], extra_lines: list[tuple[str, str]] = None) -> Text:
|
|
56
56
|
"""Create a dramatic full-screen stat reveal."""
|
|
57
57
|
text = Text()
|
|
58
58
|
text.append("\n\n\n\n\n")
|
|
@@ -60,6 +60,10 @@ def create_dramatic_stat(value: str, label: str, subtitle: str = "", color: str
|
|
|
60
60
|
text.append(f"{label}\n\n", style=Style(color=COLORS["white"], bold=True))
|
|
61
61
|
if subtitle:
|
|
62
62
|
text.append(subtitle, style=Style(color=COLORS["gray"]))
|
|
63
|
+
if extra_lines:
|
|
64
|
+
text.append("\n\n")
|
|
65
|
+
for line, line_color in extra_lines:
|
|
66
|
+
text.append(f"{line}\n", style=Style(color=line_color))
|
|
63
67
|
text.append("\n\n\n\n")
|
|
64
68
|
text.append("press [ENTER] to continue", style=Style(color=COLORS["dark"]))
|
|
65
69
|
return text
|
|
@@ -277,12 +281,12 @@ def determine_personality(stats: WrappedStats) -> dict:
|
|
|
277
281
|
|
|
278
282
|
|
|
279
283
|
def get_fun_facts(stats: WrappedStats) -> list[tuple[str, str]]:
|
|
280
|
-
"""Generate fun facts / bloopers based on stats."""
|
|
284
|
+
"""Generate fun facts / bloopers based on stats - only 3 key facts."""
|
|
281
285
|
facts = []
|
|
282
286
|
|
|
283
|
-
# Late night coding
|
|
287
|
+
# Late night coding (midnight to 5am)
|
|
284
288
|
late_night = sum(stats.hourly_distribution[0:5])
|
|
285
|
-
if late_night >
|
|
289
|
+
if late_night > 0:
|
|
286
290
|
facts.append(("๐", f"You coded after midnight {late_night:,} times. Sleep is overrated."))
|
|
287
291
|
|
|
288
292
|
# Most active day insight
|
|
@@ -290,35 +294,11 @@ def get_fun_facts(stats: WrappedStats) -> list[tuple[str, str]]:
|
|
|
290
294
|
day_name = stats.most_active_day[0].strftime("%A")
|
|
291
295
|
facts.append(("๐
", f"Your biggest day was a {day_name}. {stats.most_active_day[1]:,} messages. Epic."))
|
|
292
296
|
|
|
293
|
-
# Tool obsession
|
|
294
|
-
if stats.top_tools:
|
|
295
|
-
top_tool, count = stats.top_tools[0]
|
|
296
|
-
facts.append(("๐ง", f"You used {top_tool} {count:,} times. It's basically muscle memory now."))
|
|
297
|
-
|
|
298
|
-
# If they use Opus a lot
|
|
299
|
-
opus_count = stats.models_used.get("Opus", 0)
|
|
300
|
-
if opus_count > 1000:
|
|
301
|
-
facts.append(("๐ญ", f"You summoned Opus {opus_count:,} times. Only the best for you."))
|
|
302
|
-
|
|
303
297
|
# Streak fact
|
|
304
|
-
if stats.streak_longest >=
|
|
298
|
+
if stats.streak_longest >= 1:
|
|
305
299
|
facts.append(("๐ฅ", f"Your {stats.streak_longest}-day streak was legendary. Consistency wins."))
|
|
306
300
|
|
|
307
|
-
|
|
308
|
-
if stats.total_projects >= 3:
|
|
309
|
-
facts.append(("๐๏ธ", f"You juggled {stats.total_projects} projects. Multitasking champion."))
|
|
310
|
-
|
|
311
|
-
# Token usage perspective
|
|
312
|
-
if stats.total_tokens > 1_000_000_000:
|
|
313
|
-
books = stats.total_tokens // 100_000 # ~100k tokens per book
|
|
314
|
-
facts.append(("๐", f"You processed enough tokens for ~{books:,} books. Wow."))
|
|
315
|
-
|
|
316
|
-
# Weekend warrior
|
|
317
|
-
weekend = stats.weekday_distribution[5] + stats.weekday_distribution[6]
|
|
318
|
-
if weekend > 1000:
|
|
319
|
-
facts.append(("๐๏ธ", f"Even weekends weren't safe. {weekend:,} weekend messages."))
|
|
320
|
-
|
|
321
|
-
return facts[:5] # Limit to 5 facts
|
|
301
|
+
return facts
|
|
322
302
|
|
|
323
303
|
|
|
324
304
|
def create_fun_facts_slide(facts: list[tuple[str, str]]) -> Text:
|
|
@@ -595,29 +575,72 @@ def render_wrapped(stats: WrappedStats, console: Console | None = None, animate:
|
|
|
595
575
|
wait_for_keypress()
|
|
596
576
|
console.clear()
|
|
597
577
|
|
|
598
|
-
#
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
578
|
+
# Slide 1: Messages with date range
|
|
579
|
+
first_date = stats.first_message_date.strftime("%d %B") if stats.first_message_date else "the beginning"
|
|
580
|
+
last_date = stats.last_message_date.strftime("%d %B %Y") if stats.last_message_date else "today"
|
|
581
|
+
messages_subtitle = f"From {first_date} to {last_date}"
|
|
582
|
+
console.print(Align.center(create_dramatic_stat(
|
|
583
|
+
f"{stats.total_messages:,}", "MESSAGES", messages_subtitle, COLORS["orange"]
|
|
584
|
+
)))
|
|
585
|
+
wait_for_keypress()
|
|
586
|
+
console.clear()
|
|
605
587
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
588
|
+
# Slide 2: Averages
|
|
589
|
+
from .pricing import format_cost
|
|
590
|
+
averages_text = Text()
|
|
591
|
+
averages_text.append("\n\n\n\n")
|
|
592
|
+
averages_text.append("On average, you sent\n\n", style=Style(color=COLORS["gray"]))
|
|
593
|
+
averages_text.append(f"{stats.avg_messages_per_day:.0f}", style=Style(color=COLORS["orange"], bold=True))
|
|
594
|
+
averages_text.append(" messages per day\n", style=Style(color=COLORS["white"]))
|
|
595
|
+
averages_text.append(f"{stats.avg_messages_per_week:.0f}", style=Style(color=COLORS["blue"], bold=True))
|
|
596
|
+
averages_text.append(" messages per week\n", style=Style(color=COLORS["white"]))
|
|
597
|
+
averages_text.append(f"{stats.avg_messages_per_month:.0f}", style=Style(color=COLORS["purple"], bold=True))
|
|
598
|
+
averages_text.append(" messages per month\n\n", style=Style(color=COLORS["white"]))
|
|
599
|
+
if stats.estimated_cost is not None:
|
|
600
|
+
averages_text.append("Costing about ", style=Style(color=COLORS["gray"]))
|
|
601
|
+
averages_text.append(f"{format_cost(stats.avg_cost_per_day)}/day", style=Style(color=COLORS["green"], bold=True))
|
|
602
|
+
averages_text.append(f" ยท {format_cost(stats.avg_cost_per_week)}/week", style=Style(color=COLORS["green"]))
|
|
603
|
+
averages_text.append(f" ยท {format_cost(stats.avg_cost_per_month)}/month\n", style=Style(color=COLORS["green"]))
|
|
604
|
+
averages_text.append("\n\n\n")
|
|
605
|
+
averages_text.append("press [ENTER] to continue", style=Style(color=COLORS["dark"]))
|
|
606
|
+
console.print(Align.center(averages_text))
|
|
607
|
+
wait_for_keypress()
|
|
608
|
+
console.clear()
|
|
610
609
|
|
|
611
|
-
#
|
|
610
|
+
# Slide 3: Tokens
|
|
611
|
+
def format_tokens_dramatic(tokens: int) -> str:
|
|
612
|
+
if tokens >= 1_000_000_000:
|
|
613
|
+
return f"{tokens / 1_000_000_000:.1f} Bn"
|
|
614
|
+
if tokens >= 1_000_000:
|
|
615
|
+
return f"{tokens / 1_000_000:.0f} M"
|
|
616
|
+
if tokens >= 1_000:
|
|
617
|
+
return f"{tokens / 1_000:.0f} K"
|
|
618
|
+
return str(tokens)
|
|
619
|
+
|
|
620
|
+
tokens_text = Text()
|
|
621
|
+
tokens_text.append("\n\n\n\n\n")
|
|
622
|
+
tokens_text.append("That's\n\n", style=Style(color=COLORS["gray"]))
|
|
623
|
+
tokens_text.append(f"{format_tokens_dramatic(stats.total_tokens)}\n", style=Style(color=COLORS["green"], bold=True))
|
|
624
|
+
tokens_text.append("TOKENS\n\n", style=Style(color=COLORS["white"], bold=True))
|
|
625
|
+
tokens_text.append("processed through the AI", style=Style(color=COLORS["gray"]))
|
|
626
|
+
tokens_text.append("\n\n\n\n")
|
|
627
|
+
tokens_text.append("press [ENTER] to continue", style=Style(color=COLORS["dark"]))
|
|
628
|
+
console.print(Align.center(tokens_text))
|
|
629
|
+
wait_for_keypress()
|
|
630
|
+
console.clear()
|
|
631
|
+
|
|
632
|
+
# Slide 4: Streak + Personality (merged)
|
|
612
633
|
personality = determine_personality(stats)
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
634
|
+
streak_text = Text()
|
|
635
|
+
streak_text.append("\n\n\n\n")
|
|
636
|
+
streak_text.append(f"{stats.streak_longest}\n", style=Style(color=COLORS["blue"], bold=True))
|
|
637
|
+
streak_text.append("DAY STREAK\n\n", style=Style(color=COLORS["white"], bold=True))
|
|
638
|
+
streak_text.append(f"{personality['emoji']} ", style=Style(bold=True))
|
|
639
|
+
streak_text.append(f"{personality['title']}\n", style=Style(color=COLORS["purple"], bold=True))
|
|
640
|
+
streak_text.append(f"{personality['description']}\n", style=Style(color=COLORS["gray"]))
|
|
641
|
+
streak_text.append("\n\n\n")
|
|
642
|
+
streak_text.append("press [ENTER] to continue", style=Style(color=COLORS["dark"]))
|
|
643
|
+
console.print(Align.center(streak_text))
|
|
621
644
|
wait_for_keypress()
|
|
622
645
|
console.clear()
|
|
623
646
|
|
|
@@ -675,6 +698,10 @@ def render_wrapped(stats: WrappedStats, console: Console | None = None, animate:
|
|
|
675
698
|
)
|
|
676
699
|
console.print(lists)
|
|
677
700
|
|
|
701
|
+
# MCPs (if any)
|
|
702
|
+
if stats.top_mcps:
|
|
703
|
+
console.print(create_top_list(stats.top_mcps, "MCP Servers", COLORS["purple"]))
|
|
704
|
+
|
|
678
705
|
# Monthly cost table
|
|
679
706
|
if stats.monthly_costs:
|
|
680
707
|
console.print(create_monthly_cost_table(stats))
|
package/package.json
CHANGED