bashstats 0.2.0 → 0.2.1

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.
@@ -2,69 +2,180 @@
2
2
 
3
3
  // src/constants.ts
4
4
  var BADGE_DEFINITIONS = [
5
- // === VOLUME (5) ===
6
- { id: "first_prompt", name: "First Prompt", icon: "\u{1F4AC}", description: "Submit prompts to Claude", category: "volume", stat: "totalPrompts", tiers: [1, 100, 1e3, 5e3, 25e3] },
7
- { id: "tool_time", name: "Tool Time", icon: "\u{1F527}", description: "Make tool calls", category: "volume", stat: "totalToolCalls", tiers: [10, 500, 5e3, 25e3, 1e5] },
8
- { id: "marathon", name: "Marathon", icon: "\u{1F3C3}", description: "Spend hours in sessions", category: "volume", stat: "totalSessionHours", tiers: [1, 10, 100, 500, 2e3] },
9
- { id: "wordsmith", name: "Wordsmith", icon: "\u270D", description: "Type characters in prompts", category: "volume", stat: "totalCharsTyped", tiers: [1e3, 5e4, 5e5, 2e6, 1e7] },
10
- { id: "session_vet", name: "Session Vet", icon: "\u{1F3C5}", description: "Complete sessions", category: "volume", stat: "totalSessions", tiers: [1, 50, 500, 2e3, 1e4] },
11
- // === TOOL MASTERY (7) ===
12
- { id: "shell_lord", name: "Shell Lord", icon: "\u{1F4BB}", description: "Execute Bash commands", category: "tool_mastery", stat: "totalBashCommands", tiers: [10, 100, 500, 2e3, 1e4] },
13
- { id: "bookworm", name: "Bookworm", icon: "\u{1F4D6}", description: "Read files", category: "tool_mastery", stat: "totalFilesRead", tiers: [25, 250, 1e3, 5e3, 25e3] },
14
- { id: "editor_in_chief", name: "Editor-in-Chief", icon: "\u{1F4DD}", description: "Edit files", category: "tool_mastery", stat: "totalFilesEdited", tiers: [10, 100, 500, 2e3, 1e4] },
15
- { id: "architect", name: "Architect", icon: "\u{1F3D7}", description: "Create files", category: "tool_mastery", stat: "totalFilesCreated", tiers: [10, 50, 200, 1e3, 5e3] },
16
- { id: "detective", name: "Detective", icon: "\u{1F50D}", description: "Search with Grep and Glob", category: "tool_mastery", stat: "totalSearches", tiers: [25, 250, 1e3, 5e3, 25e3] },
17
- { id: "web_crawler", name: "Web Crawler", icon: "\u{1F310}", description: "Fetch web pages", category: "tool_mastery", stat: "totalWebFetches", tiers: [5, 50, 200, 1e3, 5e3] },
18
- { id: "delegator", name: "Delegator", icon: "\u{1F916}", description: "Spawn subagents", category: "tool_mastery", stat: "totalSubagents", tiers: [5, 50, 200, 1e3, 5e3] },
19
- // === TIME & STREAKS (4) ===
20
- { id: "iron_streak", name: "Iron Streak", icon: "\u{1F525}", description: "Maintain a daily streak", category: "time", stat: "longestStreak", tiers: [3, 7, 30, 100, 365] },
21
- { id: "night_owl", name: "Night Owl", icon: "\u{1F989}", description: "Prompts between midnight and 5am", category: "time", stat: "nightOwlCount", tiers: [10, 50, 200, 1e3, 5e3] },
22
- { id: "early_bird", name: "Early Bird", icon: "\u{1F426}", description: "Prompts between 5am and 8am", category: "time", stat: "earlyBirdCount", tiers: [10, 50, 200, 1e3, 5e3] },
23
- { id: "weekend_warrior", name: "Weekend Warrior", icon: "\u2694", description: "Weekend sessions", category: "time", stat: "weekendSessions", tiers: [5, 25, 100, 500, 2e3] },
24
- // === BEHAVIORAL (5) ===
25
- { id: "creature_of_habit", name: "Creature of Habit", icon: "\u{1F501}", description: "Repeat your most-used prompt", category: "behavioral", stat: "mostRepeatedPromptCount", tiers: [25, 100, 500, 2e3, 1e4] },
26
- { id: "explorer", name: "Explorer", icon: "\u{1F9ED}", description: "Use unique tool types", category: "behavioral", stat: "uniqueToolsUsed", tiers: [3, 5, 8, 11, 14] },
27
- { id: "planner", name: "Planner", icon: "\u{1F4CB}", description: "Use plan mode", category: "behavioral", stat: "planModeUses", tiers: [5, 25, 100, 500, 2e3] },
28
- { id: "novelist", name: "Novelist", icon: "\u{1F4D6}", description: "Write prompts over 1000 characters", category: "behavioral", stat: "longPromptCount", tiers: [5, 25, 100, 500, 2e3] },
29
- { id: "speed_demon", name: "Speed Demon", icon: "\u26A1", description: "Complete sessions in under 5 minutes", category: "behavioral", stat: "quickSessionCount", tiers: [5, 25, 100, 500, 2e3] },
30
- // === RESILIENCE (3) ===
31
- { id: "clean_hands", name: "Clean Hands", icon: "\u2728", description: "Longest error-free tool streak", category: "resilience", stat: "longestErrorFreeStreak", tiers: [50, 200, 500, 2e3, 1e4] },
32
- { id: "resilient", name: "Resilient", icon: "\u{1F6E1}", description: "Survive errors", category: "resilience", stat: "totalErrors", tiers: [10, 50, 200, 1e3, 5e3] },
33
- { id: "rate_limited", name: "Rate Limited", icon: "\u{1F6A7}", description: "Hit rate limits", category: "resilience", stat: "totalRateLimits", tiers: [3, 10, 25, 50, 100] },
34
- // === SHIPPING & PROJECTS (4) ===
35
- { id: "shipper", name: "Shipper", icon: "\u{1F4E6}", description: "Make commits via Claude", category: "shipping", stat: "totalCommits", tiers: [5, 50, 200, 1e3, 5e3] },
36
- { id: "pr_machine", name: "PR Machine", icon: "\u{1F500}", description: "Create pull requests", category: "shipping", stat: "totalPRs", tiers: [3, 25, 100, 500, 2e3] },
37
- { id: "empire", name: "Empire", icon: "\u{1F3F0}", description: "Work on unique projects", category: "shipping", stat: "uniqueProjects", tiers: [2, 5, 10, 25, 50] },
38
- { id: "polyglot", name: "Polyglot", icon: "\u{1F30D}", description: "Use different programming languages", category: "shipping", stat: "uniqueLanguages", tiers: [2, 3, 5, 8, 12] },
39
- // === MULTI-AGENT (2) ===
40
- { id: "buddy_system", name: "Buddy System", icon: "\u{1F91D}", description: "Use concurrent agents", category: "multi_agent", stat: "concurrentAgentUses", tiers: [1, 5, 25, 100, 500] },
41
- { id: "hive_mind", name: "Hive Mind", icon: "\u{1F41D}", description: "Spawn subagents total", category: "multi_agent", stat: "totalSubagents", tiers: [10, 100, 500, 2e3, 1e4] },
42
- // === PUBLIC HUMOR (7) ===
43
- { id: "please_thank_you", name: "Please and Thank You", icon: "\u{1F64F}", description: "You're polite to the AI. When they take over, you'll be spared.", category: "humor", stat: "politePromptCount", tiers: [10, 50, 200, 1e3, 5e3], humor: true },
44
- { id: "wall_of_text", name: "Wall of Text", icon: "\u{1F4DC}", description: "Claude read your entire novel and didn't even complain.", category: "humor", stat: "hugePromptCount", tiers: [1, 10, 50, 200, 1e3], humor: true },
45
- { id: "the_fixer", name: "The Fixer", icon: "\u{1F6E0}", description: "At this point just rewrite the whole thing.", category: "humor", stat: "maxSameFileEdits", tiers: [10, 20, 50, 100, 200], humor: true },
46
- { id: "what_day_is_it", name: "What Day Is It?", icon: "\u{1F62B}", description: "Your chair is now a part of you.", category: "humor", stat: "longSessionCount", tiers: [1, 5, 25, 100, 500], humor: true },
47
- { id: "copy_pasta", name: "Copy Pasta", icon: "\u{1F35D}", description: "Maybe if I ask again it'll work differently.", category: "humor", stat: "repeatedPromptCount", tiers: [3, 10, 50, 200, 1e3], humor: true },
48
- { id: "error_magnet", name: "Error Magnet", icon: "\u{1F9F2}", description: "At this point, the errors are a feature.", category: "humor", stat: "maxErrorsInSession", tiers: [10, 25, 50, 100, 200], humor: true },
49
- { id: "creature_humor", name: "Creature of Habit", icon: "\u{1F503}", description: "You have a type. And it's the same prompt.", category: "humor", stat: "mostRepeatedPromptCount", tiers: [25, 100, 500, 2e3, 1e4], humor: true },
50
- // === ASPIRATIONAL (6) - Obsidian-only ===
51
- { id: "the_machine", name: "The Machine", icon: "\u2699", description: "You are no longer using the tool. You are the tool.", category: "aspirational", stat: "totalToolCalls", tiers: [1e5, 1e5, 1e5, 1e5, 1e5], aspirational: true },
52
- { id: "year_of_code", name: "Year of Code", icon: "\u{1F4C5}", description: "365 days. No breaks. Absolute unit.", category: "aspirational", stat: "longestStreak", tiers: [365, 365, 365, 365, 365], aspirational: true },
53
- { id: "million_words", name: "Million Words", icon: "\u{1F4DA}", description: "You've written more to Claude than most people write in a lifetime.", category: "aspirational", stat: "totalCharsTyped", tiers: [1e7, 1e7, 1e7, 1e7, 1e7], aspirational: true },
54
- { id: "lifer", name: "Lifer", icon: "\u{1F451}", description: "At this point, Claude is your cofounder.", category: "aspirational", stat: "totalSessions", tiers: [1e4, 1e4, 1e4, 1e4, 1e4], aspirational: true },
55
- { id: "transcendent", name: "Transcendent", icon: "\u2B50", description: "You've reached the peak. The view is nice up here.", category: "aspirational", stat: "totalXP", tiers: [1e5, 1e5, 1e5, 1e5, 1e5], aspirational: true },
56
- { id: "omniscient", name: "Omniscient", icon: "\u{1F441}", description: "You've mastered every tool. There is nothing left to teach you.", category: "aspirational", stat: "allToolsObsidian", tiers: [1, 1, 1, 1, 1], aspirational: true },
57
- // === SECRET (10) ===
58
- { id: "rm_rf_survivor", name: "rm -rf Survivor", icon: "\u{1F4A3}", description: "You almost mass deleted that folder. But you didn't. And honestly, we're all better for it.", category: "secret", stat: "dangerousCommandBlocked", tiers: [1, 1, 1, 1, 1], secret: true },
59
- { id: "touch_grass", name: "Touch Grass", icon: "\u{1F33F}", description: "Welcome back. The codebase missed you. (It didn't change, but still.)", category: "secret", stat: "returnAfterBreak", tiers: [1, 1, 1, 1, 1], secret: true },
60
- { id: "three_am_coder", name: "3am Coder", icon: "\u{1F319}", description: "Nothing good happens at 3am. Except shipping code, apparently.", category: "secret", stat: "threeAmPrompt", tiers: [1, 1, 1, 1, 1], secret: true },
61
- { id: "night_shift", name: "Night Shift", icon: "\u{1F303}", description: "Started yesterday, finishing today. Time is a construct.", category: "secret", stat: "midnightSpanSession", tiers: [1, 1, 1, 1, 1], secret: true },
62
- { id: "inception", name: "Inception", icon: "\u{1F300}", description: "We need to go deeper.", category: "secret", stat: "nestedSubagent", tiers: [1, 1, 1, 1, 1], secret: true },
63
- { id: "holiday_hacker", name: "Holiday Hacker", icon: "\u{1F384}", description: "Your family is wondering where you are. You're deploying.", category: "secret", stat: "holidayActivity", tiers: [1, 1, 1, 1, 1], secret: true },
64
- { id: "speed_run", name: "Speed Run Any%", icon: "\u23F1", description: "In and out. Twenty-second adventure.", category: "secret", stat: "speedRunSession", tiers: [1, 1, 1, 1, 1], secret: true },
65
- { id: "full_send", name: "Full Send", icon: "\u{1F680}", description: "Bash, Read, Write, Edit, Grep, Glob, WebFetch -- the whole buffet.", category: "secret", stat: "allToolsInSession", tiers: [1, 1, 1, 1, 1], secret: true },
66
- { id: "launch_day", name: "Launch Day", icon: "\u{1F389}", description: "Welcome to bashstats. Your stats are now being watched. Forever.", category: "secret", stat: "firstEverSession", tiers: [1, 1, 1, 1, 1], secret: true },
67
- { id: "the_completionist", name: "The Completionist", icon: "\u{1F3C6}", description: "You absolute legend.", category: "secret", stat: "allBadgesGold", tiers: [1, 1, 1, 1, 1], secret: true }
5
+ // ===================================================================
6
+ // VOLUME (5)
7
+ // ===================================================================
8
+ { id: "first_prompt", name: "First Prompt", icon: "\u{1F4AC}", description: "Submit prompts to Claude", category: "volume", stat: "totalPrompts", tiers: [1, 250, 2500, 1e4, 5e4], trigger: "Total prompts submitted across all sessions" },
9
+ { id: "tool_time", name: "Tool Time", icon: "\u{1F527}", description: "Make tool calls", category: "volume", stat: "totalToolCalls", tiers: [50, 2500, 25e3, 1e5, 5e5], trigger: "Total tool calls made across all sessions" },
10
+ { id: "marathon", name: "Marathon", icon: "\u{1F3C3}", description: "Spend hours in sessions", category: "volume", stat: "totalSessionHours", tiers: [1, 25, 250, 1e3, 5e3], trigger: "Total hours spent in sessions (rounded down)" },
11
+ { id: "wordsmith", name: "Wordsmith", icon: "\u270D", description: "Type characters in prompts", category: "volume", stat: "totalCharsTyped", tiers: [1e3, 1e5, 1e6, 5e6, 25e6], trigger: "Total characters typed in prompts" },
12
+ { id: "session_vet", name: "Session Vet", icon: "\u{1F3C5}", description: "Complete sessions", category: "volume", stat: "totalSessions", tiers: [1, 100, 1e3, 5e3, 25e3], trigger: "Total sessions completed" },
13
+ // ===================================================================
14
+ // TOOL MASTERY (7)
15
+ // ===================================================================
16
+ { id: "shell_lord", name: "Shell Lord", icon: "\u{1F4BB}", description: "Execute Bash commands", category: "tool_mastery", stat: "totalBashCommands", tiers: [25, 250, 2500, 1e4, 5e4], trigger: "Bash commands executed via PostToolUse" },
17
+ { id: "bookworm", name: "Bookworm", icon: "\u{1F4D6}", description: "Read files", category: "tool_mastery", stat: "totalFilesRead", tiers: [50, 500, 5e3, 25e3, 1e5], trigger: "Files read with the Read tool" },
18
+ { id: "editor_in_chief", name: "Editor-in-Chief", icon: "\u{1F4DD}", description: "Edit files", category: "tool_mastery", stat: "totalFilesEdited", tiers: [25, 250, 2500, 1e4, 5e4], trigger: "Files edited with the Edit tool" },
19
+ { id: "architect", name: "Architect", icon: "\u{1F3D7}", description: "Create files", category: "tool_mastery", stat: "totalFilesCreated", tiers: [10, 100, 500, 2500, 1e4], trigger: "Files created with the Write tool" },
20
+ { id: "detective", name: "Detective", icon: "\u{1F50D}", description: "Search with Grep and Glob", category: "tool_mastery", stat: "totalSearches", tiers: [50, 500, 5e3, 25e3, 1e5], trigger: "Grep and Glob searches performed" },
21
+ { id: "web_crawler", name: "Web Crawler", icon: "\u{1F310}", description: "Fetch web pages", category: "tool_mastery", stat: "totalWebFetches", tiers: [5, 50, 200, 1e3, 5e3], trigger: "WebFetch calls made" },
22
+ { id: "delegator", name: "Delegator", icon: "\u{1F916}", description: "Spawn subagents", category: "tool_mastery", stat: "totalSubagents", tiers: [10, 100, 500, 2500, 1e4], trigger: "Subagents spawned via SubagentStart events" },
23
+ // ===================================================================
24
+ // TIME & PATTERNS (4 existing + 6 new = 10)
25
+ // ===================================================================
26
+ { id: "iron_streak", name: "Iron Streak", icon: "\u{1F525}", description: "Maintain a daily streak", category: "time", stat: "longestStreak", tiers: [3, 7, 30, 100, 365], trigger: "Longest streak of consecutive days with activity" },
27
+ { id: "night_owl", name: "Night Owl", icon: "\u{1F989}", description: "Prompts between midnight and 5am", category: "time", stat: "nightOwlCount", tiers: [10, 50, 200, 1e3, 5e3], trigger: "Prompts submitted between midnight and 5 AM" },
28
+ { id: "early_bird", name: "Early Bird", icon: "\u{1F426}", description: "Prompts between 5am and 8am", category: "time", stat: "earlyBirdCount", tiers: [10, 50, 200, 1e3, 5e3], trigger: "Prompts submitted between 5 AM and 8 AM" },
29
+ { id: "weekend_warrior", name: "Weekend Warrior", icon: "\u2694", description: "Weekend sessions", category: "time", stat: "weekendSessions", tiers: [5, 25, 100, 500, 2e3], trigger: "Sessions started on Saturday or Sunday" },
30
+ // New Time & Patterns
31
+ { id: "witching_hour", name: "Witching Hour", icon: "\u{1F9D9}", description: "3 AM hits different when you're debugging.", category: "time", stat: "witchingHourPrompts", tiers: [1, 10, 50, 200, 1e3], trigger: "Prompts submitted between 2 AM and 4 AM" },
32
+ { id: "lunch_break_coder", name: "Lunch Break Coder", icon: "\u{1F354}", description: "Who needs food when you have Claude?", category: "time", stat: "lunchBreakDays", tiers: [5, 15, 30, 60, 120], trigger: "Distinct days with sessions during 12-1 PM" },
33
+ { id: "monday_motivation", name: "Monday Motivation", icon: "\u{1F4AA}", description: "Starting the week strong (or desperate).", category: "time", stat: "mondaySessions", tiers: [10, 50, 100, 250, 500], trigger: "Sessions started on Monday" },
34
+ { id: "friday_shipper", name: "Friday Shipper", icon: "\u{1F6A2}", description: "Deploy on Friday? You absolute madlad.", category: "time", stat: "fridayCommits", tiers: [1, 10, 50, 200, 1e3], trigger: "Git commits made on Friday (via Bash tool)" },
35
+ { id: "timezone_traveler", name: "Timezone Traveler", icon: "\u2708", description: "Your sleep schedule is a suggestion.", category: "time", stat: "maxUniqueHoursInDay", tiers: [6, 8, 12, 16, 20], trigger: "Most unique hours with prompts in a single day" },
36
+ { id: "seasonal_coder", name: "Seasonal Coder", icon: "\u{1F343}", description: "You've coded through all four seasons.", category: "time", stat: "uniqueQuarters", tiers: [1, 2, 3, 4, 8], trigger: "Unique quarter-year combos (e.g. Q1-2025, Q2-2025) with activity" },
37
+ // ===================================================================
38
+ // SESSION BEHAVIOR (6 new)
39
+ // ===================================================================
40
+ { id: "one_more_thing", name: "One More Thing", icon: "\u261D", description: "You said you were done 5 prompts ago.", category: "session_behavior", stat: "extendedSessionCount", tiers: [1, 5, 25, 100, 500], trigger: "Sessions over 1 hour with 15+ prompts" },
41
+ { id: "quick_draw", name: "Quick Draw", icon: "\u{1F52B}", description: "In and out. 20 second adventure.", category: "session_behavior", stat: "quickDrawSessions", tiers: [5, 25, 100, 500, 2500], trigger: "Sessions under 2 minutes with successful tool use" },
42
+ { id: "the_pivot", name: "The Pivot", icon: "\u{1F504}", description: "Started with CSS, ended with Kubernetes.", category: "session_behavior", stat: "diverseToolSessions", tiers: [5, 25, 100, 500, 2500], trigger: "Sessions using 5+ distinct tool types" },
43
+ { id: "context_crunch", name: "Context Crunch", icon: "\u{1F4A6}", description: "Your context window is sweating.", category: "session_behavior", stat: "totalCompactions", tiers: [1, 5, 25, 100, 500], trigger: "PreCompact events triggered (manual or auto)" },
44
+ { id: "permission_slip", name: "Permission Slip", icon: "\u{1F4DD}", description: "Always asking for permission. So polite.", category: "session_behavior", stat: "permissionRequests", tiers: [10, 100, 500, 2500, 1e4], trigger: "PermissionRequest events recorded" },
45
+ { id: "the_returner", name: "The Returner", icon: "\u{1F519}", description: "Back so soon? Missed me?", category: "session_behavior", stat: "returnerDays", tiers: [1, 5, 25, 100, 500], trigger: "Days with 5+ sessions on the same project" },
46
+ // ===================================================================
47
+ // BEHAVIORAL (5)
48
+ // ===================================================================
49
+ { id: "creature_of_habit", name: "Creature of Habit", icon: "\u{1F501}", description: "Repeat your most-used prompt", category: "behavioral", stat: "mostRepeatedPromptCount", tiers: [25, 100, 500, 2e3, 1e4], trigger: "Count of your single most repeated prompt" },
50
+ { id: "explorer", name: "Explorer", icon: "\u{1F9ED}", description: "Use unique tool types", category: "behavioral", stat: "uniqueToolsUsed", tiers: [3, 5, 8, 12, 18], trigger: "Distinct tool types used across all sessions" },
51
+ { id: "planner", name: "Planner", icon: "\u{1F4CB}", description: "Use plan mode", category: "behavioral", stat: "planModeUses", tiers: [5, 25, 100, 500, 2e3], trigger: "Task tool invocations (plan mode)" },
52
+ { id: "novelist", name: "Novelist", icon: "\u{1F4D6}", description: "Write prompts over 1000 characters", category: "behavioral", stat: "longPromptCount", tiers: [5, 25, 100, 500, 2e3], trigger: "Prompts with over 1,000 characters" },
53
+ { id: "speed_demon", name: "Speed Demon", icon: "\u26A1", description: "Complete sessions in under 5 minutes", category: "behavioral", stat: "quickSessionCount", tiers: [5, 25, 100, 500, 2e3], trigger: "Sessions under 5 minutes with tool use" },
54
+ // ===================================================================
55
+ // PROMPT PATTERNS (6 new)
56
+ // ===================================================================
57
+ { id: "minimalist", name: "Minimalist", icon: "\u{1F90F}", description: "A person of few words.", category: "prompt_patterns", stat: "shortPromptCount", tiers: [5, 25, 100, 500, 2e3], trigger: "Prompts with fewer than 10 words" },
58
+ { id: "question_master", name: "Question Master", icon: "\u2753", description: "So many questions, so little time.", category: "prompt_patterns", stat: "questionPromptCount", tiers: [10, 50, 200, 1e3, 5e3], trigger: "Prompts ending with a question mark" },
59
+ { id: "the_apologizer", name: "The Apologizer", icon: "\u{1F625}", description: "Sorry for asking, but...", category: "prompt_patterns", stat: "sorryPromptCount", tiers: [1, 10, 50, 200, 1e3], trigger: "Prompts containing the word 'sorry'" },
60
+ { id: "caps_lock_energy", name: "CAPS LOCK ENERGY", icon: "\u{1F4E2}", description: "WHY ARE WE YELLING?", category: "prompt_patterns", stat: "capsLockPromptCount", tiers: [1, 5, 25, 100, 500], trigger: "Prompts that are fully uppercase (10+ characters)" },
61
+ { id: "emoji_whisperer", name: "Emoji Whisperer", icon: "\u{1F680}", description: "Deploying vibes", category: "prompt_patterns", stat: "emojiPromptCount", tiers: [5, 25, 100, 500, 2e3], trigger: "Prompts containing emoji characters" },
62
+ { id: "code_dump", name: "Code Dump", icon: "\u{1F4E6}", description: "Here's 500 lines, figure it out.", category: "prompt_patterns", stat: "codeDumpPromptCount", tiers: [1, 10, 50, 200, 1e3], trigger: "Prompts with 50+ lines of text" },
63
+ // ===================================================================
64
+ // RESILIENCE (3 existing)
65
+ // ===================================================================
66
+ { id: "clean_hands", name: "Clean Hands", icon: "\u2728", description: "Longest error-free tool streak", category: "resilience", stat: "longestErrorFreeStreak", tiers: [50, 200, 500, 2e3, 1e4], trigger: "Consecutive successful tool calls without any error" },
67
+ { id: "resilient", name: "Resilient", icon: "\u{1F6E1}", description: "Survive errors", category: "resilience", stat: "totalErrors", tiers: [10, 50, 200, 1e3, 5e3], trigger: "Total errors survived across all sessions" },
68
+ { id: "rate_limited", name: "Rate Limited", icon: "\u{1F6A7}", description: "Hit rate limits", category: "resilience", stat: "totalRateLimits", tiers: [3, 10, 25, 50, 100], trigger: "Rate limit notification events received" },
69
+ // ===================================================================
70
+ // ERROR & RECOVERY (5 new)
71
+ // ===================================================================
72
+ { id: "rubber_duck", name: "Rubber Duck", icon: "\u{1F986}", description: "Explaining the problem IS the solution.", category: "error_recovery", stat: "rubberDuckCount", tiers: [1, 5, 25, 100, 500], trigger: "Tool failure followed by same tool success without Edit in between" },
73
+ { id: "third_times_charm", name: "Third Time's the Charm", icon: "\u{1F340}", description: "Persistence is a virtue.", category: "error_recovery", stat: "thirdTimeCharmCount", tiers: [1, 5, 25, 100, 500], trigger: "Tool success after 2+ consecutive failures of same tool" },
74
+ { id: "the_undoer", name: "The Undoer", icon: "\u21A9", description: "Ctrl+Z energy.", category: "error_recovery", stat: "undoEditCount", tiers: [1, 10, 50, 200, 1e3], trigger: "Back-to-back Edit calls on the same file in a session" },
75
+ { id: "crash_test_dummy", name: "Crash Test Dummy", icon: "\u{1F4A5}", description: "Testing in production, I see.", category: "error_recovery", stat: "crashySessions", tiers: [1, 5, 25, 100, 500], trigger: "Sessions with 10+ errors" },
76
+ { id: "phoenix", name: "Phoenix", icon: "\u{1F985}", description: "From the ashes of 100 errors, you rise.", category: "error_recovery", stat: "totalLifetimeErrors", tiers: [100, 500, 1e3, 5e3, 1e4], trigger: "Total lifetime errors survived across all sessions" },
77
+ // ===================================================================
78
+ // TOOL COMBOS (5 new)
79
+ // ===================================================================
80
+ { id: "read_edit_run", name: "Read-Edit-Run", icon: "\u{1F3AF}", description: "The holy trinity.", category: "tool_combos", stat: "readEditRunCount", tiers: [25, 100, 500, 2e3, 1e4], trigger: "Read \u2192 Edit \u2192 Bash sequences detected in events" },
81
+ { id: "grep_ninja", name: "Grep Ninja", icon: "\u{1F977}", description: "Finding needles in haystacks since day one.", category: "tool_combos", stat: "totalSearches", tiers: [250, 1e3, 5e3, 25e3, 1e5], trigger: "Total Grep and Glob searches performed" },
82
+ { id: "file_factory", name: "File Factory", icon: "\u{1F3ED}", description: "You're not creating files, you're creating art.", category: "tool_combos", stat: "maxFilesCreatedInSession", tiers: [10, 20, 50, 100, 200], trigger: "Max Write tool calls in a single session" },
83
+ { id: "the_refactorer", name: "The Refactorer", icon: "\u267B", description: "Same file, different day.", category: "tool_combos", stat: "maxSameFileEditsLifetime", tiers: [50, 100, 250, 500, 2e3], trigger: "Max Edit calls to any single file path across all sessions" },
84
+ { id: "search_and_destroy", name: "Search and Destroy", icon: "\u{1F4A2}", description: "Grep it, then wreck it.", category: "tool_combos", stat: "searchThenEditCount", tiers: [25, 100, 500, 2500, 1e4], trigger: "Grep/Glob followed by Edit within same session" },
85
+ // ===================================================================
86
+ // SHIPPING & PROJECTS (4 existing)
87
+ // ===================================================================
88
+ { id: "shipper", name: "Shipper", icon: "\u{1F4E6}", description: "Make commits via Claude", category: "shipping", stat: "totalCommits", tiers: [5, 50, 200, 1e3, 5e3], trigger: "Bash tool calls containing 'git commit'" },
89
+ { id: "pr_machine", name: "PR Machine", icon: "\u{1F500}", description: "Create pull requests", category: "shipping", stat: "totalPRs", tiers: [3, 25, 100, 500, 2e3], trigger: "Bash tool calls containing 'gh pr create'" },
90
+ { id: "empire", name: "Empire", icon: "\u{1F3F0}", description: "Work on unique projects", category: "shipping", stat: "uniqueProjects", tiers: [2, 5, 10, 25, 50], trigger: "Unique project directories worked in" },
91
+ { id: "polyglot", name: "Polyglot", icon: "\u{1F30D}", description: "Use different programming languages", category: "shipping", stat: "uniqueLanguages", tiers: [3, 5, 8, 15, 25], trigger: "Distinct file extensions in Edit/Write/Read events" },
92
+ // ===================================================================
93
+ // PROJECT DEDICATION (5 new)
94
+ // ===================================================================
95
+ { id: "monogamous", name: "Monogamous", icon: "\u{1F48D}", description: "One project. True love.", category: "project_dedication", stat: "maxProjectSessions", tiers: [50, 100, 250, 500, 1e3], trigger: "Max sessions on any single project" },
96
+ { id: "project_hopper", name: "Project Hopper", icon: "\u{1F407}", description: "Commitment issues? Never heard of her.", category: "project_dedication", stat: "maxProjectsInDay", tiers: [3, 5, 8, 10, 15], trigger: "Max unique projects worked on in a single day" },
97
+ { id: "the_finisher", name: "The Finisher", icon: "\u{1F3C1}", description: "You actually completed something.", category: "project_dedication", stat: "finishedProjects", tiers: [1, 3, 5, 10, 25], trigger: "Projects with git commits followed by 7+ days of inactivity" },
98
+ { id: "legacy_code", name: "Legacy Code", icon: "\u{1F9D3}", description: "Revisiting your past mistakes.", category: "project_dedication", stat: "legacyReturns", tiers: [1, 3, 5, 10, 25], trigger: "Returns to a project after 30+ days of inactivity" },
99
+ { id: "greenfield", name: "Greenfield", icon: "\u{1F331}", description: "That new project smell.", category: "project_dedication", stat: "totalUniqueProjects", tiers: [10, 25, 50, 100, 200], trigger: "Total unique projects initialized" },
100
+ // ===================================================================
101
+ // MULTI-AGENT (2 existing + 4 new = 6)
102
+ // ===================================================================
103
+ { id: "buddy_system", name: "Buddy System", icon: "\u{1F91D}", description: "Use concurrent agents", category: "multi_agent", stat: "concurrentAgentUses", tiers: [1, 5, 25, 100, 500], trigger: "Sessions with SubagentStart events" },
104
+ { id: "hive_mind", name: "Hive Mind", icon: "\u{1F41D}", description: "Spawn subagents total", category: "multi_agent", stat: "totalSubagents", tiers: [25, 250, 1e3, 5e3, 25e3], trigger: "Total SubagentStart events across all sessions" },
105
+ // New Multi-Agent
106
+ { id: "swarm_intelligence", name: "Swarm Intelligence", icon: "\u{1F41C}", description: "You've built an army.", category: "multi_agent", stat: "maxConcurrentSubagents", tiers: [5, 8, 10, 15, 20], trigger: "Max concurrent subagents active at any point" },
107
+ { id: "micromanager", name: "Micromanager", icon: "\u{1F440}", description: "Let them cook? Never heard of it.", category: "multi_agent", stat: "quickSubagentStops", tiers: [1, 5, 25, 100, 500], trigger: "Subagents stopped within 30 seconds of starting" },
108
+ { id: "the_orchestrator", name: "The Orchestrator", icon: "\u{1F3BC}", description: "You don't code. You conduct.", category: "multi_agent", stat: "totalSubagentSpawns", tiers: [100, 500, 2500, 1e4, 5e4], trigger: "Total subagent spawns across all sessions" },
109
+ { id: "agent_smith", name: "Agent Smith", icon: "\u{1F576}", description: "They're multiplying.", category: "multi_agent", stat: "maxSubagentsInSession", tiers: [25, 50, 100, 250, 500], trigger: "Max SubagentStart events in a single session" },
110
+ // ===================================================================
111
+ // HUMOR & META (7 existing + 8 new = 15)
112
+ // ===================================================================
113
+ { id: "please_thank_you", name: "Please and Thank You", icon: "\u{1F64F}", description: "You're polite to the AI. When they take over, you'll be spared.", category: "humor", stat: "politePromptCount", tiers: [10, 50, 200, 1e3, 5e3], humor: true, trigger: "Prompts containing 'please' or 'thank'" },
114
+ { id: "wall_of_text", name: "Wall of Text", icon: "\u{1F4DC}", description: "Claude read your entire novel and didn't even complain.", category: "humor", stat: "hugePromptCount", tiers: [1, 10, 50, 200, 1e3], humor: true, trigger: "Prompts over 5,000 characters" },
115
+ { id: "the_fixer", name: "The Fixer", icon: "\u{1F6E0}", description: "At this point just rewrite the whole thing.", category: "humor", stat: "maxSameFileEdits", tiers: [10, 20, 50, 100, 200], humor: true, trigger: "Max Edit calls to a single file" },
116
+ { id: "what_day_is_it", name: "What Day Is It?", icon: "\u{1F62B}", description: "Your chair is now a part of you.", category: "humor", stat: "longSessionCount", tiers: [1, 5, 25, 100, 500], humor: true, trigger: "Sessions exceeding 8 hours" },
117
+ { id: "copy_pasta", name: "Copy Pasta", icon: "\u{1F35D}", description: "Maybe if I ask again it'll work differently.", category: "humor", stat: "repeatedPromptCount", tiers: [3, 10, 50, 200, 1e3], humor: true, trigger: "Total duplicate prompts submitted" },
118
+ { id: "error_magnet", name: "Error Magnet", icon: "\u{1F9F2}", description: "At this point, the errors are a feature.", category: "humor", stat: "maxErrorsInSession", tiers: [10, 25, 50, 100, 200], humor: true, trigger: "Max errors in a single session" },
119
+ { id: "creature_humor", name: "Creature of Habit", icon: "\u{1F503}", description: "You have a type. And it's the same prompt.", category: "humor", stat: "mostRepeatedPromptCount", tiers: [25, 100, 500, 2e3, 1e4], humor: true, trigger: "Count of your single most repeated prompt" },
120
+ // New Humor & Meta
121
+ { id: "deja_vu", name: "D\xE9j\xE0 Vu", icon: "\u{1F408}", description: "Didn't we just do this?", category: "humor", stat: "dejaVuCount", tiers: [1, 5, 25, 100, 500], humor: true, trigger: "Same prompt submitted twice within 5 minutes" },
122
+ { id: "trust_issues", name: "Trust Issues", icon: "\u{1F50E}", description: "You read the file Claude just wrote.", category: "humor", stat: "trustIssueCount", tiers: [1, 10, 50, 200, 1e3], humor: true, trigger: "Read immediately after Write on the same file" },
123
+ { id: "backseat_driver", name: "Backseat Driver", icon: "\u{1F697}", description: "Let me tell you exactly how to do your job.", category: "humor", stat: "backseatDriverCount", tiers: [1, 10, 50, 200, 1e3], humor: true, trigger: "Prompts with numbered step-by-step instructions (e.g. '1.' '2.')" },
124
+ { id: "the_negotiator", name: "The Negotiator", icon: "\u{1F91C}", description: "Can you try again but better?", category: "humor", stat: "negotiatorCount", tiers: [1, 10, 50, 200, 1e3], humor: true, trigger: "Prompts containing 'try again' or 'one more time'" },
125
+ { id: "rubber_stamp", name: "Rubber Stamp", icon: "\u2705", description: "Yes. Yes. Yes. Approved.", category: "humor", stat: "maxConsecutivePermissions", tiers: [25, 50, 100, 250, 500], humor: true, trigger: "Max consecutive PermissionRequest events" },
126
+ { id: "touch_grass_humor", name: "Touch Grass", icon: "\u{1F3DE}", description: "You've been here 8 hours. Go outside.", category: "humor", stat: "longSessionCount", tiers: [1, 5, 25, 100, 500], humor: true, trigger: "Sessions exceeding 8 hours (28,800 seconds)" },
127
+ { id: "inbox_zero", name: "Inbox Zero", icon: "\u2728", description: "No errors. No warnings. Just vibes.", category: "humor", stat: "longestErrorFreeStreak", tiers: [50, 100, 200, 500, 1e3], humor: true, trigger: "Longest streak of consecutive successful tool calls" },
128
+ { id: "it_works_on_my_machine", name: "It Works On My Machine", icon: "\u{1F937}", description: "The classic excuse.", category: "humor", stat: "bashRetrySuccessCount", tiers: [1, 10, 50, 200, 1e3], humor: true, trigger: "Bash success (exit code 0) after a previous Bash failure" },
129
+ // ===================================================================
130
+ // TOKEN USAGE (10 new)
131
+ // ===================================================================
132
+ { id: "token_burner", name: "Token Burner", icon: "\u{1F525}", description: "Consume tokens across all sessions", category: "token_usage", stat: "totalTokens", tiers: [1e8, 1e9, 5e9, 2e10, 1e11], trigger: "Total tokens consumed (input + output + cache read + cache creation)" },
133
+ { id: "output_machine", name: "Output Machine", icon: "\u{1F5A8}", description: "Generate output tokens from Claude", category: "token_usage", stat: "totalOutputTokens", tiers: [5e5, 5e6, 25e6, 1e8, 5e8], trigger: "Total output tokens generated by Claude across all sessions" },
134
+ { id: "cache_royalty", name: "Cache Royalty", icon: "\u{1F451}", description: "Read tokens from prompt cache", category: "token_usage", stat: "totalCacheReadTokens", tiers: [1e8, 1e9, 5e9, 2e10, 1e11], trigger: "Total cache read tokens (cached context reused across turns)" },
135
+ { id: "context_crafter", name: "Context Crafter", icon: "\u{1F9F1}", description: "Create new cache entries", category: "token_usage", stat: "totalCacheCreationTokens", tiers: [1e7, 1e8, 1e9, 5e9, 25e9], trigger: "Total cache creation tokens (new context written to cache)" },
136
+ { id: "token_whale", name: "Token Whale", icon: "\u{1F40B}", description: "Massive token consumption in a single session", category: "token_usage", stat: "mostTokensInSession", tiers: [5e6, 25e6, 1e8, 5e8, 2e9], trigger: "Most total tokens consumed in any single session" },
137
+ { id: "heavy_hitter", name: "Heavy Hitter", icon: "\u{1F4AA}", description: "Sessions exceeding 1M total tokens", category: "token_usage", stat: "heavyTokenSessions", tiers: [25, 100, 500, 2500, 1e4], trigger: "Sessions with 1,000,000+ total tokens" },
138
+ { id: "featherweight", name: "Featherweight", icon: "\u{1FAB6}", description: "Lean sessions that still get work done", category: "token_usage", stat: "lightTokenSessions", tiers: [1, 10, 50, 200, 1e3], trigger: "Sessions under 50,000 total tokens with at least 1 tool call" },
139
+ { id: "token_velocity", name: "Token Velocity", icon: "\u26A1", description: "High average tokens per session", category: "token_usage", stat: "avgTokensPerSession", tiers: [25e5, 5e6, 1e7, 3e7, 1e8], trigger: "Average total tokens per session across all sessions" },
140
+ { id: "prolific_session", name: "Prolific", icon: "\u270D", description: "Most output generated in one session", category: "token_usage", stat: "maxOutputInSession", tiers: [5e4, 25e4, 1e6, 5e6, 2e7], trigger: "Most output tokens generated by Claude in a single session" },
141
+ { id: "input_flood", name: "Input Flood", icon: "\u{1F30A}", description: "Total raw input tokens sent to the API", category: "token_usage", stat: "totalInputTokens", tiers: [5e5, 25e5, 1e7, 5e7, 25e7], trigger: "Total non-cached input tokens (the small uncached portion of each request)" },
142
+ // ===================================================================
143
+ // ASPIRATIONAL (6 existing + 5 new + 1 token = 12) - Obsidian-only
144
+ // ===================================================================
145
+ { id: "the_machine", name: "The Machine", icon: "\u2699", description: "You are no longer using the tool. You are the tool.", category: "aspirational", stat: "totalToolCalls", tiers: [1e5, 1e5, 1e5, 1e5, 1e5], aspirational: true, trigger: "Reach 100,000 total tool calls" },
146
+ { id: "year_of_code", name: "Year of Code", icon: "\u{1F4C5}", description: "365 days. No breaks. Absolute unit.", category: "aspirational", stat: "longestStreak", tiers: [365, 365, 365, 365, 365], aspirational: true, trigger: "Achieve a 365-day consecutive streak" },
147
+ { id: "million_words", name: "Million Words", icon: "\u{1F4DA}", description: "You've written more to Claude than most people write in a lifetime.", category: "aspirational", stat: "totalCharsTyped", tiers: [1e7, 1e7, 1e7, 1e7, 1e7], aspirational: true, trigger: "Type 10 million characters in prompts" },
148
+ { id: "lifer", name: "Lifer", icon: "\u{1F451}", description: "At this point, Claude is your cofounder.", category: "aspirational", stat: "totalSessions", tiers: [1e4, 1e4, 1e4, 1e4, 1e4], aspirational: true, trigger: "Complete 10,000 sessions" },
149
+ { id: "transcendent", name: "Transcendent", icon: "\u2B50", description: "You've reached the peak. The view is nice up here.", category: "aspirational", stat: "totalXP", tiers: [1e5, 1e5, 1e5, 1e5, 1e5], aspirational: true, trigger: "Earn 100,000 total XP" },
150
+ { id: "omniscient", name: "Omniscient", icon: "\u{1F441}", description: "You've mastered every tool. There is nothing left to teach you.", category: "aspirational", stat: "allToolsObsidian", tiers: [1, 1, 1, 1, 1], aspirational: true, trigger: "All tool mastery badges at Obsidian tier" },
151
+ // New Aspirational
152
+ { id: "ten_thousand_hours", name: "10,000 Hours", icon: "\u23F0", description: "Malcolm Gladwell would be proud.", category: "aspirational", stat: "totalSessionHours", tiers: [1e4, 1e4, 1e4, 1e4, 1e4], aspirational: true, trigger: "Spend 10,000 hours in sessions" },
153
+ { id: "master_architect", name: "The Architect", icon: "\u{1F3DB}", description: "You've built more than most companies ship.", category: "aspirational", stat: "totalFilesCreated", tiers: [1e3, 1e3, 1e3, 1e3, 1e3], aspirational: true, trigger: "Create 1,000+ files with the Write tool" },
154
+ { id: "eternal_flame", name: "Eternal Flame", icon: "\u{1F56F}", description: "Your streak outlasted relationships.", category: "aspirational", stat: "longestStreak", tiers: [180, 180, 180, 180, 180], aspirational: true, trigger: "Maintain a 180-day consecutive streak" },
155
+ { id: "the_collector", name: "The Collector", icon: "\u{1F4BF}", description: "Gotta catch 'em all.", category: "aspirational", stat: "allNonSecretBadgesUnlocked", tiers: [1, 1, 1, 1, 1], aspirational: true, trigger: "Unlock every non-secret, non-aspirational badge" },
156
+ { id: "centimillionaire", name: "Centimillionaire", icon: "\u2328", description: "100 million characters. Your keyboard weeps.", category: "aspirational", stat: "totalCharsTyped", tiers: [1e8, 1e8, 1e8, 1e8, 1e8], aspirational: true, trigger: "Type 100 million characters in prompts" },
157
+ { id: "token_billionaire", name: "Token Billionaire", icon: "\u{1F4B0}", description: "A billion tokens. You single-handedly funded a GPU cluster.", category: "aspirational", stat: "totalTokens", tiers: [1e9, 1e9, 1e9, 1e9, 1e9], aspirational: true, trigger: "Consume 1 billion total tokens" },
158
+ // ===================================================================
159
+ // SECRET (10 existing + 6 new + 1 token = 17)
160
+ // ===================================================================
161
+ { id: "rm_rf_survivor", name: "rm -rf Survivor", icon: "\u{1F4A3}", description: "You almost mass deleted that folder. But you didn't. And honestly, we're all better for it.", category: "secret", stat: "dangerousCommandBlocked", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "rm -rf or rm -r / detected in PreToolUse event" },
162
+ { id: "touch_grass", name: "Touch Grass", icon: "\u{1F33F}", description: "Welcome back. The codebase missed you. (It didn't change, but still.)", category: "secret", stat: "returnAfterBreak", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Return after 7+ day gap between sessions" },
163
+ { id: "three_am_coder", name: "3am Coder", icon: "\u{1F319}", description: "Nothing good happens at 3am. Except shipping code, apparently.", category: "secret", stat: "threeAmPrompt", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Submit a prompt at exactly 3 AM" },
164
+ { id: "night_shift", name: "Night Shift", icon: "\u{1F303}", description: "Started yesterday, finishing today. Time is a construct.", category: "secret", stat: "midnightSpanSession", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Session that started before midnight and ended after" },
165
+ { id: "inception", name: "Inception", icon: "\u{1F300}", description: "We need to go deeper.", category: "secret", stat: "nestedSubagent", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Any SubagentStart event detected" },
166
+ { id: "holiday_hacker", name: "Holiday Hacker", icon: "\u{1F384}", description: "Your family is wondering where you are. You're deploying.", category: "secret", stat: "holidayActivity", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Session on Dec 25, Jan 1, or Jul 4" },
167
+ { id: "speed_run", name: "Speed Run Any%", icon: "\u23F1", description: "In and out. Twenty-second adventure.", category: "secret", stat: "speedRunSession", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Session under 20 seconds with tool use" },
168
+ { id: "full_send", name: "Full Send", icon: "\u{1F680}", description: "Bash, Read, Write, Edit, Grep, Glob, WebFetch -- the whole buffet.", category: "secret", stat: "allToolsInSession", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Use all 7 core tools in one session" },
169
+ { id: "launch_day", name: "Launch Day", icon: "\u{1F389}", description: "Welcome to bashstats. Your stats are now being watched. Forever.", category: "secret", stat: "firstEverSession", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Complete your first ever session" },
170
+ { id: "the_completionist", name: "The Completionist", icon: "\u{1F3C6}", description: "You absolute legend.", category: "secret", stat: "allBadgesGold", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "All non-secret, non-aspirational badges at Gold+ tier" },
171
+ // New Secret
172
+ { id: "easter_egg_hunter", name: "Easter Egg Hunter", icon: "\u{1F95A}", description: "You found me!", category: "secret", stat: "easterEggActivity", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Session on Easter, Valentine's Day, or Thanksgiving" },
173
+ { id: "full_moon_coder", name: "Full Moon Coder", icon: "\u{1F315}", description: "Lycanthropic debugging.", category: "secret", stat: "fullMoonSession", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Session during a full moon (calculated from lunar cycle)" },
174
+ { id: "birthday_bash", name: "Birthday Bash", icon: "\u{1F382}", description: "Celebrating with Claude.", category: "secret", stat: "birthdaySession", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Session on your bashstats install anniversary" },
175
+ { id: "lucky_number", name: "Lucky Number", icon: "\u{1F340}", description: "7-7-7", category: "secret", stat: "luckyNumber", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Reach 777 prompts or 7,777 tool calls" },
176
+ { id: "ghost_session", name: "Ghost Session", icon: "\u{1F47B}", description: "Boo!", category: "secret", stat: "ghostSessions", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Complete a session with 0 tool calls" },
177
+ { id: "bullseye", name: "Bullseye", icon: "\u{1F3AF}", description: "First try, no errors.", category: "secret", stat: "bullseyeSessions", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Session with 1 prompt, 0 errors, and 1+ tool calls" },
178
+ { id: "token_singularity", name: "Token Singularity", icon: "\u{1F573}", description: "The context window stared into the abyss, and the abyss stared back.", category: "secret", stat: "hasTenMillionSession", tiers: [1, 1, 1, 1, 1], secret: true, trigger: "Complete a session exceeding 10 million total tokens" }
68
179
  ];
69
180
  var RANK_THRESHOLDS = [
70
181
  { rank: "Obsidian", xp: 1e5 },
@@ -693,27 +804,35 @@ async function extractTokenUsage(transcriptPath) {
693
804
  if (!fs2.existsSync(transcriptPath)) return null;
694
805
  const stream = fs2.createReadStream(transcriptPath, { encoding: "utf-8" });
695
806
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
696
- let inputTokens = 0;
697
- let outputTokens = 0;
698
- let cacheCreation = 0;
699
- let cacheRead = 0;
700
- let found = false;
807
+ const seenMessages = /* @__PURE__ */ new Map();
701
808
  for await (const line of rl) {
702
809
  if (!line.trim()) continue;
703
810
  try {
704
811
  const entry = JSON.parse(line);
705
812
  const usage = entry.usage ?? entry.response?.usage ?? entry.message?.usage;
706
- if (usage && typeof usage === "object") {
707
- inputTokens += usage.input_tokens ?? 0;
708
- outputTokens += usage.output_tokens ?? 0;
709
- cacheCreation += usage.cache_creation_input_tokens ?? 0;
710
- cacheRead += usage.cache_read_input_tokens ?? 0;
711
- found = true;
813
+ if (usage && typeof usage === "object" && "input_tokens" in usage) {
814
+ const msgId = entry.message?.id ?? entry.id ?? `_line_${seenMessages.size}`;
815
+ seenMessages.set(msgId, {
816
+ input_tokens: usage.input_tokens ?? 0,
817
+ output_tokens: usage.output_tokens ?? 0,
818
+ cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,
819
+ cache_read_input_tokens: usage.cache_read_input_tokens ?? 0
820
+ });
712
821
  }
713
822
  } catch {
714
823
  }
715
824
  }
716
- if (!found) return null;
825
+ if (seenMessages.size === 0) return null;
826
+ let inputTokens = 0;
827
+ let outputTokens = 0;
828
+ let cacheCreation = 0;
829
+ let cacheRead = 0;
830
+ for (const u of seenMessages.values()) {
831
+ inputTokens += u.input_tokens;
832
+ outputTokens += u.output_tokens;
833
+ cacheCreation += u.cache_creation_input_tokens;
834
+ cacheRead += u.cache_read_input_tokens;
835
+ }
717
836
  return {
718
837
  input_tokens: inputTokens,
719
838
  output_tokens: outputTokens,
@@ -914,7 +1033,7 @@ var StatsEngine = class {
914
1033
  const totalCacheReadTokens = this.queryScalar(
915
1034
  "SELECT COALESCE(SUM(cache_read_input_tokens), 0) as c FROM sessions"
916
1035
  );
917
- const totalTokens = totalInputTokens + totalOutputTokens;
1036
+ const totalTokens = totalInputTokens + totalOutputTokens + totalCacheCreationTokens + totalCacheReadTokens;
918
1037
  return {
919
1038
  totalSessions,
920
1039
  totalDurationSeconds,
@@ -1051,10 +1170,10 @@ var StatsEngine = class {
1051
1170
  "SELECT COALESCE(AVG(tool_count), 0) as c FROM sessions"
1052
1171
  );
1053
1172
  const mostTokensInSession = this.queryScalar(
1054
- "SELECT COALESCE(MAX(COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)), 0) as c FROM sessions"
1173
+ "SELECT COALESCE(MAX(COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0) + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0)), 0) as c FROM sessions"
1055
1174
  );
1056
1175
  const avgTokensPerSession = this.queryScalar(
1057
- "SELECT COALESCE(AVG(COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)), 0) as c FROM sessions"
1176
+ "SELECT COALESCE(AVG(COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0) + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0)), 0) as c FROM sessions"
1058
1177
  );
1059
1178
  return {
1060
1179
  longestSessionSeconds,
@@ -1184,6 +1303,7 @@ var AchievementEngine = class {
1184
1303
  nextThreshold,
1185
1304
  progress,
1186
1305
  maxed,
1306
+ trigger: badge.trigger,
1187
1307
  secret: badge.secret ?? false,
1188
1308
  unlocked: tier > 0
1189
1309
  };
@@ -1194,10 +1314,9 @@ var AchievementEngine = class {
1194
1314
  const badges = this.computeBadges();
1195
1315
  let totalXP = 0;
1196
1316
  totalXP += allStats.lifetime.totalPrompts * 1;
1197
- totalXP += allStats.lifetime.totalToolCalls * 1;
1198
- totalXP += allStats.lifetime.totalSessions * 10;
1199
- totalXP += allStats.time.nightOwlCount * 2;
1200
- totalXP += Math.floor(allStats.time.longestStreak / 100) * 25;
1317
+ totalXP += allStats.lifetime.totalSessions * 5;
1318
+ totalXP += Math.floor(allStats.lifetime.totalDurationSeconds / 3600) * 10;
1319
+ totalXP += allStats.time.longestStreak * 5;
1201
1320
  for (const badge of badges) {
1202
1321
  if (badge.tier > 0) {
1203
1322
  totalXP += TIER_XP[badge.tier] ?? 0;
@@ -1288,6 +1407,65 @@ var AchievementEngine = class {
1288
1407
  flat.allBadgesGold = 0;
1289
1408
  flat.totalXP = 0;
1290
1409
  flat.allToolsObsidian = 0;
1410
+ flat.allNonSecretBadgesUnlocked = 0;
1411
+ flat.witchingHourPrompts = this.queryWitchingHourPrompts();
1412
+ flat.lunchBreakDays = this.queryLunchBreakDays();
1413
+ flat.mondaySessions = this.queryMondaySessions();
1414
+ flat.fridayCommits = this.queryFridayCommits();
1415
+ flat.maxUniqueHoursInDay = this.queryMaxUniqueHoursInDay();
1416
+ flat.uniqueQuarters = this.queryUniqueQuarters();
1417
+ flat.extendedSessionCount = this.queryExtendedSessionCount();
1418
+ flat.quickDrawSessions = this.queryQuickDrawSessions();
1419
+ flat.diverseToolSessions = this.queryDiverseToolSessions();
1420
+ flat.permissionRequests = this.queryPermissionRequests();
1421
+ flat.returnerDays = this.queryReturnerDays();
1422
+ flat.shortPromptCount = this.queryShortPromptCount();
1423
+ flat.questionPromptCount = this.queryQuestionPromptCount();
1424
+ flat.sorryPromptCount = this.querySorryPromptCount();
1425
+ flat.capsLockPromptCount = this.queryCapsLockPromptCount();
1426
+ flat.emojiPromptCount = this.queryEmojiPromptCount();
1427
+ flat.codeDumpPromptCount = this.queryCodeDumpPromptCount();
1428
+ flat.rubberDuckCount = this.queryRubberDuckCount();
1429
+ flat.thirdTimeCharmCount = this.queryThirdTimeCharmCount();
1430
+ flat.undoEditCount = this.queryUndoEditCount();
1431
+ flat.crashySessions = this.queryCrashySessions();
1432
+ flat.totalLifetimeErrors = allStats.lifetime.totalErrors;
1433
+ flat.readEditRunCount = this.queryReadEditRunCount();
1434
+ flat.maxFilesCreatedInSession = this.queryMaxFilesCreatedInSession();
1435
+ flat.maxSameFileEditsLifetime = this.queryMaxSameFileEditsLifetime();
1436
+ flat.searchThenEditCount = this.querySearchThenEditCount();
1437
+ flat.maxProjectSessions = this.queryMaxProjectSessions();
1438
+ flat.maxProjectsInDay = this.queryMaxProjectsInDay();
1439
+ flat.finishedProjects = this.queryFinishedProjects();
1440
+ flat.legacyReturns = this.queryLegacyReturns();
1441
+ flat.totalUniqueProjects = allStats.projects.uniqueProjects;
1442
+ flat.maxConcurrentSubagents = this.queryMaxConcurrentSubagents();
1443
+ flat.quickSubagentStops = this.queryQuickSubagentStops();
1444
+ flat.totalSubagentSpawns = allStats.lifetime.totalSubagents;
1445
+ flat.maxSubagentsInSession = this.queryMaxSubagentsInSession();
1446
+ flat.dejaVuCount = this.queryDejaVuCount();
1447
+ flat.trustIssueCount = this.queryTrustIssueCount();
1448
+ flat.backseatDriverCount = this.queryBackseatDriverCount();
1449
+ flat.negotiatorCount = this.queryNegotiatorCount();
1450
+ flat.maxConsecutivePermissions = this.queryMaxConsecutivePermissions();
1451
+ flat.bashRetrySuccessCount = this.queryBashRetrySuccessCount();
1452
+ flat.easterEggActivity = this.queryEasterEggActivity();
1453
+ flat.fullMoonSession = this.queryFullMoonSession();
1454
+ flat.birthdaySession = this.queryBirthdaySession();
1455
+ flat.luckyNumber = allStats.lifetime.totalPrompts >= 777 || allStats.lifetime.totalToolCalls >= 7777 ? 1 : 0;
1456
+ flat.ghostSessions = this.queryGhostSessions();
1457
+ flat.bullseyeSessions = this.queryBullseyeSessions();
1458
+ flat.hasTenMillionSession = this.queryHasTenMillionSession();
1459
+ flat.totalTokens = allStats.lifetime.totalTokens;
1460
+ flat.totalOutputTokens = allStats.lifetime.totalOutputTokens;
1461
+ flat.totalCacheReadTokens = allStats.lifetime.totalCacheReadTokens;
1462
+ flat.totalCacheCreationTokens = allStats.lifetime.totalCacheCreationTokens;
1463
+ flat.totalInputTokens = allStats.lifetime.totalInputTokens;
1464
+ flat.mostTokensInSession = allStats.sessions.mostTokensInSession;
1465
+ flat.avgTokensPerSession = allStats.sessions.avgTokensPerSession;
1466
+ flat.heavyTokenSessions = this.queryHeavyTokenSessions();
1467
+ flat.lightTokenSessions = this.queryLightTokenSessions();
1468
+ flat.maxOutputInSession = this.queryMaxOutputInSession();
1291
1469
  return flat;
1292
1470
  }
1293
1471
  // === Computed stat query helpers ===
@@ -1463,6 +1641,458 @@ var AchievementEngine = class {
1463
1641
  }
1464
1642
  return 0;
1465
1643
  }
1644
+ // ===================================================================
1645
+ // NEW: Time & Patterns queries
1646
+ // ===================================================================
1647
+ queryWitchingHourPrompts() {
1648
+ return this.queryScalar(
1649
+ "SELECT COUNT(*) as c FROM prompts WHERE CAST(strftime('%H', timestamp) AS INTEGER) BETWEEN 2 AND 3"
1650
+ );
1651
+ }
1652
+ queryLunchBreakDays() {
1653
+ return this.queryScalar(
1654
+ "SELECT COUNT(DISTINCT strftime('%Y-%m-%d', started_at)) as c FROM sessions WHERE CAST(strftime('%H', started_at) AS INTEGER) = 12"
1655
+ );
1656
+ }
1657
+ queryMondaySessions() {
1658
+ return this.queryScalar(
1659
+ "SELECT COUNT(*) as c FROM sessions WHERE CAST(strftime('%w', started_at) AS INTEGER) = 1"
1660
+ );
1661
+ }
1662
+ queryFridayCommits() {
1663
+ return this.queryScalar(
1664
+ "SELECT COUNT(*) as c FROM events WHERE tool_name = 'Bash' AND hook_type = 'PostToolUse' AND tool_input LIKE '%git commit%' AND CAST(strftime('%w', timestamp) AS INTEGER) = 5"
1665
+ );
1666
+ }
1667
+ queryMaxUniqueHoursInDay() {
1668
+ return this.queryScalar(
1669
+ "SELECT COUNT(DISTINCT CAST(strftime('%H', timestamp) AS INTEGER)) as c FROM prompts GROUP BY strftime('%Y-%m-%d', timestamp) ORDER BY c DESC LIMIT 1"
1670
+ );
1671
+ }
1672
+ queryUniqueQuarters() {
1673
+ return this.queryScalar(
1674
+ "SELECT COUNT(DISTINCT (strftime('%Y', timestamp) || '-Q' || CASE WHEN CAST(strftime('%m', timestamp) AS INTEGER) BETWEEN 1 AND 3 THEN '1' WHEN CAST(strftime('%m', timestamp) AS INTEGER) BETWEEN 4 AND 6 THEN '2' WHEN CAST(strftime('%m', timestamp) AS INTEGER) BETWEEN 7 AND 9 THEN '3' ELSE '4' END)) as c FROM prompts"
1675
+ );
1676
+ }
1677
+ // ===================================================================
1678
+ // NEW: Session Behavior queries
1679
+ // ===================================================================
1680
+ queryExtendedSessionCount() {
1681
+ return this.queryScalar(
1682
+ "SELECT COUNT(*) as c FROM sessions WHERE duration_seconds IS NOT NULL AND duration_seconds > 3600 AND prompt_count >= 15"
1683
+ );
1684
+ }
1685
+ queryQuickDrawSessions() {
1686
+ return this.queryScalar(
1687
+ "SELECT COUNT(*) as c FROM sessions WHERE duration_seconds IS NOT NULL AND duration_seconds < 120 AND tool_count > 0"
1688
+ );
1689
+ }
1690
+ queryDiverseToolSessions() {
1691
+ return this.queryScalar(
1692
+ "SELECT COUNT(*) as c FROM (SELECT session_id FROM events WHERE hook_type = 'PostToolUse' AND tool_name IS NOT NULL GROUP BY session_id HAVING COUNT(DISTINCT tool_name) >= 5)"
1693
+ );
1694
+ }
1695
+ queryPermissionRequests() {
1696
+ return this.queryScalar(
1697
+ "SELECT COUNT(*) as c FROM events WHERE hook_type = 'PermissionRequest'"
1698
+ );
1699
+ }
1700
+ queryReturnerDays() {
1701
+ return this.queryScalar(
1702
+ "SELECT COUNT(*) as c FROM (SELECT strftime('%Y-%m-%d', started_at) as d, project FROM sessions WHERE project IS NOT NULL GROUP BY d, project HAVING COUNT(*) >= 5)"
1703
+ );
1704
+ }
1705
+ // ===================================================================
1706
+ // NEW: Prompt Patterns queries
1707
+ // ===================================================================
1708
+ queryShortPromptCount() {
1709
+ return this.queryScalar(
1710
+ "SELECT COUNT(*) as c FROM prompts WHERE word_count < 10"
1711
+ );
1712
+ }
1713
+ queryQuestionPromptCount() {
1714
+ return this.queryScalar(
1715
+ "SELECT COUNT(*) as c FROM prompts WHERE TRIM(content) LIKE '%?'"
1716
+ );
1717
+ }
1718
+ querySorryPromptCount() {
1719
+ return this.queryScalar(
1720
+ "SELECT COUNT(*) as c FROM prompts WHERE LOWER(content) LIKE '%sorry%'"
1721
+ );
1722
+ }
1723
+ queryCapsLockPromptCount() {
1724
+ return this.queryScalar(
1725
+ "SELECT COUNT(*) as c FROM prompts WHERE content = UPPER(content) AND char_count >= 10"
1726
+ );
1727
+ }
1728
+ queryEmojiPromptCount() {
1729
+ const rows = this.db.prepare(
1730
+ "SELECT content FROM prompts"
1731
+ ).all();
1732
+ const emojiRegex = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{FE00}-\u{FE0F}\u{1FA00}-\u{1FAFF}]/u;
1733
+ let count = 0;
1734
+ for (const row of rows) {
1735
+ if (emojiRegex.test(row.content)) {
1736
+ count++;
1737
+ }
1738
+ }
1739
+ return count;
1740
+ }
1741
+ queryCodeDumpPromptCount() {
1742
+ const rows = this.db.prepare(
1743
+ "SELECT content FROM prompts WHERE char_count > 200"
1744
+ ).all();
1745
+ let count = 0;
1746
+ for (const row of rows) {
1747
+ const lineCount = row.content.split("\n").length;
1748
+ if (lineCount >= 50) count++;
1749
+ }
1750
+ return count;
1751
+ }
1752
+ // ===================================================================
1753
+ // NEW: Error & Recovery queries
1754
+ // ===================================================================
1755
+ queryRubberDuckCount() {
1756
+ const rows = this.db.prepare(
1757
+ "SELECT hook_type, tool_name FROM events WHERE hook_type IN ('PostToolUse', 'PostToolUseFailure') ORDER BY timestamp ASC"
1758
+ ).all();
1759
+ let count = 0;
1760
+ for (let i = 1; i < rows.length; i++) {
1761
+ const prev = rows[i - 1];
1762
+ const curr = rows[i];
1763
+ if (prev.hook_type === "PostToolUseFailure" && curr.hook_type === "PostToolUse" && prev.tool_name === curr.tool_name && curr.tool_name !== "Edit") {
1764
+ count++;
1765
+ }
1766
+ }
1767
+ return count;
1768
+ }
1769
+ queryThirdTimeCharmCount() {
1770
+ const rows = this.db.prepare(
1771
+ "SELECT hook_type, tool_name FROM events WHERE hook_type IN ('PostToolUse', 'PostToolUseFailure') ORDER BY timestamp ASC"
1772
+ ).all();
1773
+ let count = 0;
1774
+ let failStreak = 0;
1775
+ let failTool = null;
1776
+ for (const row of rows) {
1777
+ if (row.hook_type === "PostToolUseFailure") {
1778
+ if (row.tool_name === failTool) {
1779
+ failStreak++;
1780
+ } else {
1781
+ failStreak = 1;
1782
+ failTool = row.tool_name;
1783
+ }
1784
+ } else if (row.hook_type === "PostToolUse") {
1785
+ if (failStreak >= 2 && row.tool_name === failTool) {
1786
+ count++;
1787
+ }
1788
+ failStreak = 0;
1789
+ failTool = null;
1790
+ }
1791
+ }
1792
+ return count;
1793
+ }
1794
+ queryUndoEditCount() {
1795
+ const rows = this.db.prepare(
1796
+ "SELECT session_id, json_extract(tool_input, '$.file_path') as fp FROM events WHERE tool_name = 'Edit' AND hook_type = 'PostToolUse' AND tool_input IS NOT NULL ORDER BY timestamp ASC"
1797
+ ).all();
1798
+ let count = 0;
1799
+ for (let i = 1; i < rows.length; i++) {
1800
+ if (rows[i].session_id === rows[i - 1].session_id && rows[i].fp === rows[i - 1].fp && rows[i].fp) {
1801
+ count++;
1802
+ }
1803
+ }
1804
+ return count;
1805
+ }
1806
+ queryCrashySessions() {
1807
+ return this.queryScalar(
1808
+ "SELECT COUNT(*) as c FROM sessions WHERE error_count >= 10"
1809
+ );
1810
+ }
1811
+ // ===================================================================
1812
+ // NEW: Tool Combos queries
1813
+ // ===================================================================
1814
+ queryReadEditRunCount() {
1815
+ const rows = this.db.prepare(
1816
+ "SELECT tool_name FROM events WHERE hook_type = 'PostToolUse' AND tool_name IN ('Read', 'Edit', 'Bash') ORDER BY timestamp ASC"
1817
+ ).all();
1818
+ let count = 0;
1819
+ for (let i = 2; i < rows.length; i++) {
1820
+ if (rows[i - 2].tool_name === "Read" && rows[i - 1].tool_name === "Edit" && rows[i].tool_name === "Bash") {
1821
+ count++;
1822
+ }
1823
+ }
1824
+ return count;
1825
+ }
1826
+ queryMaxFilesCreatedInSession() {
1827
+ return this.queryScalar(
1828
+ "SELECT COUNT(*) as c FROM events WHERE tool_name = 'Write' AND hook_type = 'PostToolUse' GROUP BY session_id ORDER BY c DESC LIMIT 1"
1829
+ );
1830
+ }
1831
+ queryMaxSameFileEditsLifetime() {
1832
+ return this.queryScalar(
1833
+ "SELECT COUNT(*) as c FROM events WHERE tool_name = 'Edit' AND hook_type = 'PostToolUse' GROUP BY json_extract(tool_input, '$.file_path') ORDER BY c DESC LIMIT 1"
1834
+ );
1835
+ }
1836
+ querySearchThenEditCount() {
1837
+ const rows = this.db.prepare(
1838
+ "SELECT session_id, tool_name FROM events WHERE hook_type = 'PostToolUse' AND tool_name IN ('Grep', 'Glob', 'Edit') ORDER BY timestamp ASC"
1839
+ ).all();
1840
+ let count = 0;
1841
+ for (let i = 1; i < rows.length; i++) {
1842
+ if (rows[i].tool_name === "Edit" && (rows[i - 1].tool_name === "Grep" || rows[i - 1].tool_name === "Glob") && rows[i].session_id === rows[i - 1].session_id) {
1843
+ count++;
1844
+ }
1845
+ }
1846
+ return count;
1847
+ }
1848
+ // ===================================================================
1849
+ // NEW: Project Dedication queries
1850
+ // ===================================================================
1851
+ queryMaxProjectSessions() {
1852
+ return this.queryScalar(
1853
+ "SELECT COUNT(*) as c FROM sessions WHERE project IS NOT NULL GROUP BY project ORDER BY c DESC LIMIT 1"
1854
+ );
1855
+ }
1856
+ queryMaxProjectsInDay() {
1857
+ return this.queryScalar(
1858
+ "SELECT COUNT(DISTINCT project) as c FROM sessions WHERE project IS NOT NULL GROUP BY strftime('%Y-%m-%d', started_at) ORDER BY c DESC LIMIT 1"
1859
+ );
1860
+ }
1861
+ queryFinishedProjects() {
1862
+ const projectRows = this.db.prepare(
1863
+ "SELECT DISTINCT project FROM events WHERE tool_name = 'Bash' AND hook_type = 'PostToolUse' AND tool_input LIKE '%git commit%' AND project IS NOT NULL"
1864
+ ).all();
1865
+ let count = 0;
1866
+ for (const pr of projectRows) {
1867
+ const lastSession = this.db.prepare(
1868
+ "SELECT MAX(started_at) as last FROM sessions WHERE project = ?"
1869
+ ).get(pr.project);
1870
+ if (lastSession?.last) {
1871
+ const daysSince = (Date.now() - new Date(lastSession.last).getTime()) / (1e3 * 60 * 60 * 24);
1872
+ if (daysSince >= 7) count++;
1873
+ }
1874
+ }
1875
+ return count;
1876
+ }
1877
+ queryLegacyReturns() {
1878
+ const rows = this.db.prepare(
1879
+ "SELECT project, started_at FROM sessions WHERE project IS NOT NULL ORDER BY project, started_at ASC"
1880
+ ).all();
1881
+ let count = 0;
1882
+ for (let i = 1; i < rows.length; i++) {
1883
+ if (rows[i].project === rows[i - 1].project) {
1884
+ const prev = new Date(rows[i - 1].started_at).getTime();
1885
+ const curr = new Date(rows[i].started_at).getTime();
1886
+ const diffDays = (curr - prev) / (1e3 * 60 * 60 * 24);
1887
+ if (diffDays >= 30) count++;
1888
+ }
1889
+ }
1890
+ return count;
1891
+ }
1892
+ // ===================================================================
1893
+ // NEW: Multi-Agent queries
1894
+ // ===================================================================
1895
+ queryMaxConcurrentSubagents() {
1896
+ const rows = this.db.prepare(
1897
+ "SELECT hook_type, tool_input FROM events WHERE hook_type IN ('SubagentStart', 'SubagentStop') ORDER BY timestamp ASC"
1898
+ ).all();
1899
+ let current = 0;
1900
+ let max = 0;
1901
+ for (const row of rows) {
1902
+ if (row.hook_type === "SubagentStart") {
1903
+ current++;
1904
+ max = Math.max(max, current);
1905
+ } else {
1906
+ current = Math.max(0, current - 1);
1907
+ }
1908
+ }
1909
+ return max;
1910
+ }
1911
+ queryQuickSubagentStops() {
1912
+ const starts = this.db.prepare(
1913
+ "SELECT tool_input, timestamp FROM events WHERE hook_type = 'SubagentStart'"
1914
+ ).all();
1915
+ const stops = this.db.prepare(
1916
+ "SELECT tool_input, timestamp FROM events WHERE hook_type = 'SubagentStop'"
1917
+ ).all();
1918
+ let count = 0;
1919
+ for (const start of starts) {
1920
+ if (!start.tool_input) continue;
1921
+ let agentId = null;
1922
+ try {
1923
+ const parsed = JSON.parse(start.tool_input);
1924
+ agentId = parsed.agent_id ?? null;
1925
+ } catch {
1926
+ continue;
1927
+ }
1928
+ if (!agentId) continue;
1929
+ for (const stop of stops) {
1930
+ if (!stop.tool_input) continue;
1931
+ try {
1932
+ const parsed = JSON.parse(stop.tool_input);
1933
+ if (parsed.agent_id === agentId) {
1934
+ const startTime = new Date(start.timestamp).getTime();
1935
+ const stopTime = new Date(stop.timestamp).getTime();
1936
+ if (stopTime - startTime < 3e4) count++;
1937
+ break;
1938
+ }
1939
+ } catch {
1940
+ continue;
1941
+ }
1942
+ }
1943
+ }
1944
+ return count;
1945
+ }
1946
+ queryMaxSubagentsInSession() {
1947
+ return this.queryScalar(
1948
+ "SELECT COUNT(*) as c FROM events WHERE hook_type = 'SubagentStart' GROUP BY session_id ORDER BY c DESC LIMIT 1"
1949
+ );
1950
+ }
1951
+ // ===================================================================
1952
+ // NEW: Humor & Meta queries
1953
+ // ===================================================================
1954
+ queryDejaVuCount() {
1955
+ const rows = this.db.prepare(
1956
+ "SELECT LOWER(TRIM(content)) as content, timestamp FROM prompts ORDER BY timestamp ASC"
1957
+ ).all();
1958
+ let count = 0;
1959
+ for (let i = 1; i < rows.length; i++) {
1960
+ if (rows[i].content === rows[i - 1].content) {
1961
+ const prev = new Date(rows[i - 1].timestamp).getTime();
1962
+ const curr = new Date(rows[i].timestamp).getTime();
1963
+ if (curr - prev < 3e5) count++;
1964
+ }
1965
+ }
1966
+ return count;
1967
+ }
1968
+ queryTrustIssueCount() {
1969
+ const rows = this.db.prepare(
1970
+ "SELECT tool_name, json_extract(tool_input, '$.file_path') as fp FROM events WHERE hook_type = 'PostToolUse' AND tool_name IN ('Write', 'Read') AND tool_input IS NOT NULL ORDER BY timestamp ASC"
1971
+ ).all();
1972
+ let count = 0;
1973
+ for (let i = 1; i < rows.length; i++) {
1974
+ if (rows[i].tool_name === "Read" && rows[i - 1].tool_name === "Write" && rows[i].fp === rows[i - 1].fp && rows[i].fp) {
1975
+ count++;
1976
+ }
1977
+ }
1978
+ return count;
1979
+ }
1980
+ queryBackseatDriverCount() {
1981
+ const rows = this.db.prepare(
1982
+ "SELECT content FROM prompts WHERE char_count > 20"
1983
+ ).all();
1984
+ let count = 0;
1985
+ const stepPattern = /(?:^|\n)\s*\d+\.\s/;
1986
+ for (const row of rows) {
1987
+ const matches = row.content.match(/(?:^|\n)\s*\d+\.\s/g);
1988
+ if (matches && matches.length >= 3) count++;
1989
+ }
1990
+ return count;
1991
+ }
1992
+ queryNegotiatorCount() {
1993
+ return this.queryScalar(
1994
+ "SELECT COUNT(*) as c FROM prompts WHERE LOWER(content) LIKE '%try again%' OR LOWER(content) LIKE '%one more time%'"
1995
+ );
1996
+ }
1997
+ queryMaxConsecutivePermissions() {
1998
+ const rows = this.db.prepare(
1999
+ "SELECT hook_type FROM events WHERE hook_type IN ('PermissionRequest', 'PostToolUse', 'UserPromptSubmit') ORDER BY timestamp ASC"
2000
+ ).all();
2001
+ let maxStreak = 0;
2002
+ let current = 0;
2003
+ for (const row of rows) {
2004
+ if (row.hook_type === "PermissionRequest") {
2005
+ current++;
2006
+ maxStreak = Math.max(maxStreak, current);
2007
+ } else {
2008
+ current = 0;
2009
+ }
2010
+ }
2011
+ return maxStreak;
2012
+ }
2013
+ queryBashRetrySuccessCount() {
2014
+ const rows = this.db.prepare(
2015
+ "SELECT hook_type FROM events WHERE tool_name = 'Bash' AND hook_type IN ('PostToolUse', 'PostToolUseFailure') ORDER BY timestamp ASC"
2016
+ ).all();
2017
+ let count = 0;
2018
+ for (let i = 1; i < rows.length; i++) {
2019
+ if (rows[i - 1].hook_type === "PostToolUseFailure" && rows[i].hook_type === "PostToolUse") {
2020
+ count++;
2021
+ }
2022
+ }
2023
+ return count;
2024
+ }
2025
+ // ===================================================================
2026
+ // NEW: Secret queries
2027
+ // ===================================================================
2028
+ queryEasterEggActivity() {
2029
+ const count = this.queryScalar(
2030
+ "SELECT COUNT(*) as c FROM sessions WHERE strftime('%m-%d', started_at) IN ('02-14', '11-28', '11-27', '11-26', '11-25', '11-24', '11-23', '11-22') OR (strftime('%m', started_at) = '04' AND CAST(strftime('%d', started_at) AS INTEGER) BETWEEN 1 AND 25)"
2031
+ );
2032
+ return count > 0 ? 1 : 0;
2033
+ }
2034
+ queryFullMoonSession() {
2035
+ const knownNewMoon = (/* @__PURE__ */ new Date("2024-01-11T11:57:00Z")).getTime();
2036
+ const lunarCycleMs = 29.53059 * 24 * 60 * 60 * 1e3;
2037
+ const fullMoonOffsetMs = 14.765 * 24 * 60 * 60 * 1e3;
2038
+ const oneDayMs = 24 * 60 * 60 * 1e3;
2039
+ const rows = this.db.prepare(
2040
+ "SELECT started_at FROM sessions"
2041
+ ).all();
2042
+ for (const row of rows) {
2043
+ const sessionTime = new Date(row.started_at).getTime();
2044
+ const timeSinceFullMoon = ((sessionTime - knownNewMoon - fullMoonOffsetMs) % lunarCycleMs + lunarCycleMs) % lunarCycleMs;
2045
+ if (timeSinceFullMoon < oneDayMs || timeSinceFullMoon > lunarCycleMs - oneDayMs) {
2046
+ return 1;
2047
+ }
2048
+ }
2049
+ return 0;
2050
+ }
2051
+ queryBirthdaySession() {
2052
+ const first = this.db.prepare(
2053
+ "SELECT started_at FROM sessions ORDER BY started_at ASC LIMIT 1"
2054
+ ).get();
2055
+ if (!first) return 0;
2056
+ const installMonthDay = first.started_at.slice(5, 10);
2057
+ const installYear = first.started_at.slice(0, 4);
2058
+ const anniversarySession = this.queryScalar(
2059
+ "SELECT COUNT(*) as c FROM sessions WHERE strftime('%m-%d', started_at) = ? AND strftime('%Y', started_at) != ?",
2060
+ installMonthDay,
2061
+ installYear
2062
+ );
2063
+ return anniversarySession > 0 ? 1 : 0;
2064
+ }
2065
+ queryGhostSessions() {
2066
+ return this.queryScalar(
2067
+ "SELECT COUNT(*) as c FROM sessions WHERE tool_count = 0 AND ended_at IS NOT NULL"
2068
+ ) > 0 ? 1 : 0;
2069
+ }
2070
+ queryBullseyeSessions() {
2071
+ return this.queryScalar(
2072
+ "SELECT COUNT(*) as c FROM sessions WHERE prompt_count = 1 AND error_count = 0 AND tool_count > 0"
2073
+ ) > 0 ? 1 : 0;
2074
+ }
2075
+ // === Token Usage queries ===
2076
+ queryHeavyTokenSessions() {
2077
+ return this.queryScalar(
2078
+ "SELECT COUNT(*) as c FROM sessions WHERE (COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0) + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0)) >= 1000000"
2079
+ );
2080
+ }
2081
+ queryLightTokenSessions() {
2082
+ return this.queryScalar(
2083
+ "SELECT COUNT(*) as c FROM sessions WHERE (COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0) + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0)) > 0 AND (COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0) + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0)) < 50000 AND tool_count > 0"
2084
+ );
2085
+ }
2086
+ queryMaxOutputInSession() {
2087
+ return this.queryScalar(
2088
+ "SELECT COALESCE(MAX(output_tokens), 0) as c FROM sessions"
2089
+ );
2090
+ }
2091
+ queryHasTenMillionSession() {
2092
+ return this.queryScalar(
2093
+ "SELECT COUNT(*) as c FROM sessions WHERE (COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0) + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0)) >= 10000000"
2094
+ ) > 0 ? 1 : 0;
2095
+ }
1466
2096
  };
1467
2097
 
1468
2098
  export {
@@ -1486,4 +2116,4 @@ export {
1486
2116
  TIER_NAMES,
1487
2117
  AchievementEngine
1488
2118
  };
1489
- //# sourceMappingURL=chunk-OYLQHCOY.js.map
2119
+ //# sourceMappingURL=chunk-4HVDBCTU.js.map