feat: add more achievements

This commit is contained in:
2026-01-19 19:39:36 -08:00
parent d72ca7a975
commit da566f408e
4 changed files with 416 additions and 4 deletions
+202
View File
@@ -55,6 +55,28 @@ pub enum AchievementId {
SpeedCoder, // 10 code blocks in 10 minutes
ClaudeConnoisseur, // Used all Claude models
MarathonCoder, // 10k tokens in one session
// Relationship & Greetings
GoodMorning, // Say "good morning"
GoodNight, // Say "good night" or "goodnight"
ThankYou, // Say "thank you" or "thanks"
LoveYou, // Say "love you" or "ily"
// Personality & Fun
EmojiUser, // Use an emoji in a message
QuestionMaster, // Use "?" in 20 messages
CapsLock, // Send a message in ALL CAPS
PleaseAndThankYou, // Use "please" in messages
// Git & Development
GitGuru, // Use git commands 10 times
TestWriter, // Create test files
Debugger, // Fix bugs (messages with "fix", "bug", "error")
// Tool Mastery
BashMaster, // Use Bash tool 50 times
FileExplorer, // Use Read tool 100 times
SearchExpert, // Use Grep tool 50 times
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -361,9 +383,164 @@ pub fn get_achievement_info(id: &AchievementId) -> Achievement {
icon: "🏃‍♂️".to_string(),
unlocked_at: None,
},
// Relationship & Greetings
AchievementId::GoodMorning => Achievement {
id: id.clone(),
name: "Good Morning!".to_string(),
description: "Greeted Hikari with a good morning".to_string(),
icon: "🌅".to_string(),
unlocked_at: None,
},
AchievementId::GoodNight => Achievement {
id: id.clone(),
name: "Good Night".to_string(),
description: "Said good night to Hikari".to_string(),
icon: "🌙".to_string(),
unlocked_at: None,
},
AchievementId::ThankYou => Achievement {
id: id.clone(),
name: "Grateful Heart".to_string(),
description: "Thanked Hikari for her help".to_string(),
icon: "💝".to_string(),
unlocked_at: None,
},
AchievementId::LoveYou => Achievement {
id: id.clone(),
name: "Love Connection".to_string(),
description: "Expressed love to Hikari".to_string(),
icon: "💕".to_string(),
unlocked_at: None,
},
// Personality & Fun
AchievementId::EmojiUser => Achievement {
id: id.clone(),
name: "Emoji Enthusiast".to_string(),
description: "Used an emoji in your message".to_string(),
icon: "😊".to_string(),
unlocked_at: None,
},
AchievementId::QuestionMaster => Achievement {
id: id.clone(),
name: "Question Master".to_string(),
description: "Asked 20 questions".to_string(),
icon: "".to_string(),
unlocked_at: None,
},
AchievementId::CapsLock => Achievement {
id: id.clone(),
name: "CAPS LOCK ENGAGED".to_string(),
description: "SENT A MESSAGE IN ALL CAPS".to_string(),
icon: "📢".to_string(),
unlocked_at: None,
},
AchievementId::PleaseAndThankYou => Achievement {
id: id.clone(),
name: "Polite Programmer".to_string(),
description: "Said please in a request".to_string(),
icon: "🎩".to_string(),
unlocked_at: None,
},
// Git & Development
AchievementId::GitGuru => Achievement {
id: id.clone(),
name: "Git Guru".to_string(),
description: "Used git commands 10 times".to_string(),
icon: "🌿".to_string(),
unlocked_at: None,
},
AchievementId::TestWriter => Achievement {
id: id.clone(),
name: "Test Writer".to_string(),
description: "Created test files".to_string(),
icon: "🧪".to_string(),
unlocked_at: None,
},
AchievementId::Debugger => Achievement {
id: id.clone(),
name: "Bug Squasher".to_string(),
description: "Fixed bugs and errors".to_string(),
icon: "🐛".to_string(),
unlocked_at: None,
},
// Tool Mastery
AchievementId::BashMaster => Achievement {
id: id.clone(),
name: "Bash Master".to_string(),
description: "Used Bash tool 50 times".to_string(),
icon: "🐚".to_string(),
unlocked_at: None,
},
AchievementId::FileExplorer => Achievement {
id: id.clone(),
name: "File Explorer".to_string(),
description: "Read 100 files".to_string(),
icon: "📂".to_string(),
unlocked_at: None,
},
AchievementId::SearchExpert => Achievement {
id: id.clone(),
name: "Search Expert".to_string(),
description: "Used Grep tool 50 times".to_string(),
icon: "🔎".to_string(),
unlocked_at: None,
},
}
}
// Check achievements based on message content
pub fn check_message_achievements(
message: &str,
progress: &mut AchievementProgress,
) -> Vec<AchievementId> {
let mut newly_unlocked = Vec::new();
let message_lower = message.to_lowercase();
println!("Checking message achievements for: {}", message);
// Relationship & Greetings
if message_lower.contains("good morning") && progress.unlock(AchievementId::GoodMorning) {
newly_unlocked.push(AchievementId::GoodMorning);
}
if (message_lower.contains("good night") || message_lower.contains("goodnight"))
&& progress.unlock(AchievementId::GoodNight) {
newly_unlocked.push(AchievementId::GoodNight);
}
if (message_lower.contains("thank you") || message_lower.contains("thanks") || message_lower.contains("thx"))
&& progress.unlock(AchievementId::ThankYou) {
newly_unlocked.push(AchievementId::ThankYou);
}
if (message_lower.contains("love you") || message_lower.contains("ily"))
&& progress.unlock(AchievementId::LoveYou) {
newly_unlocked.push(AchievementId::LoveYou);
}
// Personality & Fun
if message.chars().any(|c| c as u32 >= 0x1F300) && progress.unlock(AchievementId::EmojiUser) {
newly_unlocked.push(AchievementId::EmojiUser);
}
if message == message.to_uppercase() && message.len() > 5
&& message.chars().any(|c| c.is_alphabetic())
&& progress.unlock(AchievementId::CapsLock) {
newly_unlocked.push(AchievementId::CapsLock);
}
if message_lower.contains("please") && progress.unlock(AchievementId::PleaseAndThankYou) {
newly_unlocked.push(AchievementId::PleaseAndThankYou);
}
// Git & Development patterns in messages
if (message_lower.contains("fix") || message_lower.contains("bug") || message_lower.contains("error"))
&& progress.unlock(AchievementId::Debugger) {
newly_unlocked.push(AchievementId::Debugger);
}
newly_unlocked
}
// Check which achievements should be unlocked based on current stats
pub fn check_achievements(
stats: &crate::stats::UsageStats,
@@ -489,6 +666,31 @@ pub fn check_achievements(
// Claude Connoisseur - check model usage
// TODO: Track different Claude models used
// Tool mastery achievements
if let Some(bash_count) = stats.tools_usage.get("Bash") {
if *bash_count >= 50 && progress.unlock(AchievementId::BashMaster) {
newly_unlocked.push(AchievementId::BashMaster);
}
}
if let Some(read_count) = stats.tools_usage.get("Read") {
if *read_count >= 100 && progress.unlock(AchievementId::FileExplorer) {
newly_unlocked.push(AchievementId::FileExplorer);
}
}
if let Some(grep_count) = stats.tools_usage.get("Grep") {
if *grep_count >= 50 && progress.unlock(AchievementId::SearchExpert) {
newly_unlocked.push(AchievementId::SearchExpert);
}
}
// Git Guru - check git command usage in Bash
if let Some(bash_count) = stats.tools_usage.get("Bash") {
if *bash_count >= 10 && progress.unlock(AchievementId::GitGuru) {
// TODO: More specific git command tracking
newly_unlocked.push(AchievementId::GitGuru);
}
}
// Time-based achievements
if let Some(session_start) = progress.session_start {
let hour = session_start.hour();
+23 -3
View File
@@ -91,7 +91,7 @@ impl WslBridge {
}
pub async fn new_with_loaded_achievements(app: &tauri::AppHandle) -> Self {
let mut bridge = Self::new();
let bridge = Self::new();
// Load saved achievements into the stats
let achievements = crate::achievements::load_achievements(app).await;
@@ -613,15 +613,35 @@ fn process_json_line(line: &str, app: &AppHandle, stats: &Arc<RwLock<UsageStats>
emit_state_change(app, state, None);
}
ClaudeMessage::User { .. } => {
ClaudeMessage::User { message } => {
// Increment message count for user messages
stats.write().increment_messages();
// Extract text content from the message
let message_text = message.content.iter()
.filter_map(|block| match block {
crate::types::ContentBlock::Text { text } => Some(text.clone()),
_ => None,
})
.collect::<Vec<String>>()
.join(" ");
// Check achievements after user message
let newly_unlocked = {
let mut stats_guard = stats.write();
println!("User sent message, checking achievements...");
stats_guard.check_achievements()
// Check message-based achievements
let mut unlocked = crate::achievements::check_message_achievements(
&message_text,
&mut stats_guard.achievements,
);
// Check stats-based achievements
let stats_unlocked = stats_guard.check_achievements();
unlocked.extend(stats_unlocked);
unlocked
};
// Emit achievement events for any newly unlocked achievements
+168
View File
@@ -283,6 +283,154 @@ const achievementDefinitions: Record<AchievementId, Omit<Achievement, 'unlocked'
rarity: 'epic',
maxProgress: 10000,
},
// Relationship & Greetings
GoodMorning: {
id: 'GoodMorning',
name: 'Good Morning!',
description: 'Greeted Hikari with a good morning',
icon: '🌅',
rarity: 'common',
maxProgress: 1,
},
GoodNight: {
id: 'GoodNight',
name: 'Sweet Dreams',
description: 'Said good night to Hikari',
icon: '🌙',
rarity: 'common',
maxProgress: 1,
},
ThankYou: {
id: 'ThankYou',
name: 'Grateful Heart',
description: 'Thanked Hikari for her help',
icon: '🙏',
rarity: 'common',
maxProgress: 1,
},
LoveYou: {
id: 'LoveYou',
name: 'Heartfelt',
description: 'Expressed love to Hikari',
icon: '💕',
rarity: 'rare',
maxProgress: 1,
},
// Personality & Fun
EmojiUser: {
id: 'EmojiUser',
name: 'Emoji Master',
description: 'Used 20+ emojis in messages',
icon: '😄',
rarity: 'common',
maxProgress: 20,
},
CapsLock: {
id: 'CapsLock',
name: 'CAPS LOCK',
description: 'SENT A MESSAGE IN ALL CAPS',
icon: '🔊',
rarity: 'common',
maxProgress: 1,
},
QuestionMaster: {
id: 'QuestionMaster',
name: 'Question Master',
description: 'Asked 50 questions',
icon: '❓',
rarity: 'common',
maxProgress: 50,
},
PleaseAndThankYou: {
id: 'PleaseAndThankYou',
name: 'Polite Programmer',
description: 'Always says please and thank you',
icon: '🎩',
rarity: 'common',
maxProgress: 10,
},
// Git & Development
CommitMaster: {
id: 'CommitMaster',
name: 'Commit Master',
description: 'Made 100 commits through Hikari',
icon: '📝',
rarity: 'rare',
maxProgress: 100,
},
PRO: {
id: 'PRO',
name: 'PRO',
description: 'Created 10 pull requests',
icon: '🔀',
rarity: 'rare',
maxProgress: 10,
},
Reviewer: {
id: 'Reviewer',
name: 'Code Reviewer',
description: 'Reviewed 10 pull requests',
icon: '👀',
rarity: 'rare',
maxProgress: 10,
},
IssueTracker: {
id: 'IssueTracker',
name: 'Issue Tracker',
description: 'Created 25 issues',
icon: '🎯',
rarity: 'rare',
maxProgress: 25,
},
GitGuru: {
id: 'GitGuru',
name: 'Git Guru',
description: 'Mastered git operations',
icon: '🌲',
rarity: 'epic',
},
// Tool Mastery
BashMaster: {
id: 'BashMaster',
name: 'Bash Master',
description: 'Used bash commands 100 times',
icon: '💻',
rarity: 'rare',
maxProgress: 100,
},
FileExplorer: {
id: 'FileExplorer',
name: 'File Explorer',
description: 'Explored files 100 times',
icon: '📂',
rarity: 'common',
maxProgress: 100,
},
SearchExpert: {
id: 'SearchExpert',
name: 'Search Expert',
description: 'Mastered advanced search queries',
icon: '🔎',
rarity: 'rare',
},
AgentCommander: {
id: 'AgentCommander',
name: 'Agent Commander',
description: 'Used task agents effectively',
icon: '🤖',
rarity: 'rare',
},
MCPMaster: {
id: 'MCPMaster',
name: 'MCP Master',
description: 'Mastered MCP tool usage',
icon: '🛠️',
rarity: 'epic',
},
};
// Initialize all achievements as locked
@@ -447,6 +595,26 @@ export const achievementCategories = [
description: 'Your coding session achievements',
ids: ['QuickSession', 'FocusedWork', 'DeepDive', 'MarathonSession', 'MarathonCoder'] as AchievementId[],
},
{
name: 'Relationship & Greetings',
description: 'Our special moments together',
ids: ['GoodMorning', 'GoodNight', 'ThankYou', 'LoveYou'] as AchievementId[],
},
{
name: 'Personality & Fun',
description: 'Express yourself!',
ids: ['EmojiUser', 'CapsLock', 'QuestionMaster', 'PleaseAndThankYou'] as AchievementId[],
},
{
name: 'Git & Development',
description: 'Version control mastery',
ids: ['CommitMaster', 'PRO', 'Reviewer', 'IssueTracker', 'GitGuru'] as AchievementId[],
},
{
name: 'Tool Mastery',
description: 'Master of all tools',
ids: ['BashMaster', 'FileExplorer', 'SearchExpert', 'AgentCommander', 'MCPMaster'] as AchievementId[],
},
{
name: 'Special',
description: 'Unique accomplishments',
+23 -1
View File
@@ -50,7 +50,29 @@ export type AchievementId =
| 'Polyglot' // 5+ languages in one session
| 'SpeedCoder' // 10 code blocks in 10 minutes
| 'ClaudeConnoisseur' // Used all Claude models
| 'MarathonCoder';
| 'MarathonCoder' // 10k tokens in one session
// Relationship & Greetings
| 'GoodMorning' // Said good morning
| 'GoodNight' // Said good night
| 'ThankYou' // Said thank you
| 'LoveYou' // Said love you
// Personality & Fun
| 'EmojiUser' // Used 20+ emojis
| 'CapsLock' // ALL CAPS MESSAGE
| 'QuestionMaster' // Asked 50 questions
| 'PleaseAndThankYou' // Polite user
// Git & Development
| 'CommitMaster' // 100 commits
| 'PRO' // Created 10 PRs
| 'Reviewer' // Reviewed 10 PRs
| 'IssueTracker' // Created 25 issues
| 'GitGuru' // Used git commands
// Tool Mastery
| 'BashMaster' // Used bash 100 times
| 'FileExplorer' // Searched files 100 times
| 'SearchExpert' // Advanced searches
| 'AgentCommander' // Used task agents
| 'MCPMaster'; // Used MCP tools
export interface Achievement {
id: AchievementId;