UNPKG

virtue-cli

Version:

Personal character development CLI tool for tracking virtues and philosophical alignment

467 lines (459 loc) • 23.6 kB
import { intro, outro, select, text, confirm, log, spinner } from '@clack/prompts'; import { ClaudeService } from '../services/ClaudeService.js'; import { AutoSaveManager } from '../services/AutoSaveManager.js'; import { calculateAlignment, calculateCoherence, formatDateDisplay } from '../utils/helpers.js'; import picocolors from 'picocolors'; export class MonthlyDeepDive { db; profile; claudeService; autoSaveManager; constructor(db, profile) { this.db = db; this.profile = profile; this.claudeService = new ClaudeService(); this.autoSaveManager = new AutoSaveManager(db); } async run() { intro(picocolors.cyan('šŸ” Monthly Deep-Dive Assessment')); try { // Check if there's enough data for a meaningful assessment const hasEnoughData = await this.checkDataSufficiency(); if (!hasEnoughData) { log.warning('Not enough data for a comprehensive assessment. Need at least 5 entries this month.'); outro(picocolors.yellow('šŸ’” Complete more daily check-ins and return for your deep-dive!')); return; } const assessmentType = await select({ message: 'What type of monthly assessment would you like?', options: [ { value: 'comprehensive', label: 'šŸŽÆ Comprehensive Review', hint: 'Full analysis with AI insights and intervention planning' }, { value: 'reflection', label: '🧘 Guided Reflection', hint: 'Structured self-reflection on the month' }, { value: 'progress', label: 'šŸ“ˆ Progress Analysis', hint: 'Focus on growth and achievement patterns' }, { value: 'planning', label: 'šŸ“‹ Next Month Planning', hint: 'Set intentions and goals for upcoming month' } ] }); switch (assessmentType) { case 'comprehensive': await this.runComprehensiveAssessment(); break; case 'reflection': await this.runGuidedReflection(); break; case 'progress': await this.runProgressAnalysis(); break; case 'planning': await this.runMonthlyPlanning(); break; } } catch (error) { log.error(`Assessment failed: ${error}`); outro(picocolors.red('Assessment failed')); } } async checkDataSufficiency() { const currentMonth = new Date().toISOString().slice(0, 7); // YYYY-MM const monthStart = new Date(currentMonth + '-01'); const entries = this.db.getEntriesForProfile(this.profile.id, monthStart); return entries.length >= 5; } async runComprehensiveAssessment() { const s = spinner(); s.start('Analyzing your monthly data...'); const monthlyAnalysis = await this.generateMonthlyAnalysis(); s.stop('Analysis complete!'); // Display analysis this.displayMonthlyOverview(monthlyAnalysis); this.displayVirtueAnalysis(monthlyAnalysis); // AI-powered insights if available if (this.claudeService.isConfigured()) { const aiInsights = await this.generateAIInsights(monthlyAnalysis); this.displayAIInsights(aiInsights); // Intervention planning const wantsIntervention = await confirm({ message: 'Would you like to create an intervention plan for next month?', initialValue: true }); if (wantsIntervention && typeof wantsIntervention !== 'symbol') { const interventionPlan = await this.createInterventionPlan(monthlyAnalysis); this.displayInterventionPlan(interventionPlan); await this.saveDeepDive(monthlyAnalysis, interventionPlan); } } else { log.info('šŸ’” AI insights and intervention planning available when run via Claude Code'); await this.saveDeepDive(monthlyAnalysis, null); } outro(picocolors.green('🌟 Monthly assessment complete! Keep growing!')); } async runGuidedReflection() { console.log(picocolors.bold('\n🧘 Guided Monthly Reflection')); console.log('═'.repeat(50)); const reflectionQuestions = [ 'What virtue did you embody most naturally this month?', 'Which virtue challenged you the most, and what did you learn?', 'Describe a moment this month when you felt most aligned with your values.', 'What patterns do you notice in your daily check-ins?', 'How has your understanding of virtue ethics deepened?', 'What would you tell your past self from the beginning of this month?', 'What intention do you want to set for next month?' ]; const reflections = []; for (let i = 0; i < reflectionQuestions.length; i++) { const question = reflectionQuestions[i]; console.log(picocolors.cyan(`\n${i + 1}. ${question}`)); const reflection = await text({ message: 'Your reflection:', placeholder: 'Take your time to reflect deeply...' }); if (typeof reflection === 'string') { reflections.push(reflection); } } // Display reflection summary console.log(picocolors.bold('\nšŸ“ Your Monthly Reflection Summary')); console.log('═'.repeat(50)); reflectionQuestions.forEach((question, index) => { console.log(picocolors.yellow(`\n${index + 1}. ${question}`)); console.log(picocolors.gray(reflections[index] || 'No response')); }); const saveReflection = await confirm({ message: 'Save this reflection to your monthly records?', initialValue: true }); if (saveReflection && typeof saveReflection !== 'symbol') { const monthlyAnalysis = await this.generateMonthlyAnalysis(); await this.saveDeepDive(monthlyAnalysis, null, reflections); } outro(picocolors.green('šŸ™ Beautiful reflection completed!')); } async runProgressAnalysis() { const s = spinner(); s.start('Analyzing your progress patterns...'); const monthlyAnalysis = await this.generateMonthlyAnalysis(); s.stop('Progress analysis complete!'); console.log(picocolors.bold('\nšŸ“ˆ Monthly Progress Analysis')); console.log('═'.repeat(50)); // Overall progress console.log(picocolors.bold('\nšŸŽÆ Overall Progress')); console.log(`šŸ“Š Total Check-ins: ${picocolors.cyan(monthlyAnalysis.monthData.totalEntries.toString())}`); console.log(`šŸŽŖ Average Coherence: ${this.formatCoherence(monthlyAnalysis.monthData.averageCoherence)}`); console.log(`⚔ Consistency Score: ${this.formatCoherence(monthlyAnalysis.monthData.consistencyScore)}`); // Best and challenging days console.log(picocolors.bold('\nšŸ† Highlights')); console.log(`🌟 Best Day: ${formatDateDisplay(new Date(monthlyAnalysis.monthData.bestDay.date))} (${this.formatCoherence(monthlyAnalysis.monthData.bestDay.coherence)})`); console.log(`šŸ’Ŗ Growth Day: ${formatDateDisplay(new Date(monthlyAnalysis.monthData.challengingDay.date))} (${this.formatCoherence(monthlyAnalysis.monthData.challengingDay.coherence)})`); // Virtue progress console.log(picocolors.bold('\nšŸŽÆ Virtue Progress')); monthlyAnalysis.virtueAnalysis.forEach(va => { const trendIcon = va.trend === 'improving' ? 'šŸ“ˆ' : va.trend === 'declining' ? 'šŸ“‰' : 'šŸ“Š'; const trendColor = va.trend === 'improving' ? picocolors.green : va.trend === 'declining' ? picocolors.red : picocolors.yellow; console.log(`${va.virtue.emoji} ${va.virtue.name}: ${this.formatCoherence(va.monthlyAverage)} ${trendIcon} ${trendColor(va.trend)}`); }); // Growth areas if (monthlyAnalysis.overallThemes.length > 0) { console.log(picocolors.bold('\n🌱 Growth Themes')); monthlyAnalysis.overallThemes.forEach(theme => { console.log(`• ${theme}`); }); } outro(picocolors.green('šŸ“ˆ Progress analysis complete!')); } async runMonthlyPlanning() { console.log(picocolors.bold('\nšŸ“‹ Next Month Planning')); console.log('═'.repeat(50)); // Get current month analysis for context const monthlyAnalysis = await this.generateMonthlyAnalysis(); // Select focus virtue for next month const virtueOptions = monthlyAnalysis.virtueAnalysis.map(va => ({ value: va.virtue.id, label: `${va.virtue.emoji} ${va.virtue.name}`, hint: `Current: ${(va.monthlyAverage * 100).toFixed(1)}% - ${va.trend}` })); const focusVirtueId = await select({ message: 'Which virtue would you like to focus on next month?', options: virtueOptions }); const focusVirtue = monthlyAnalysis.virtueAnalysis.find(va => va.virtue.id === focusVirtueId)?.virtue; if (!focusVirtue) return; // Set specific goals const goals = await text({ message: `What specific goals do you have for ${focusVirtue.name} next month?`, placeholder: 'e.g., Practice daily meditation, Read one philosophy book...' }); // Daily practices const dailyPractices = await text({ message: 'What daily practices will support this virtue?', placeholder: 'e.g., 10 minutes of reflection, mindful walking...' }); // Accountability measures const accountability = await text({ message: 'How will you track progress and stay accountable?', placeholder: 'e.g., Weekly check-ins, journal entries, friend updates...' }); // Display plan console.log(picocolors.bold(`\nšŸŽÆ Your ${focusVirtue.name} Focus Plan`)); console.log('═'.repeat(50)); console.log(`šŸŽŖ Focus Virtue: ${focusVirtue.emoji} ${focusVirtue.name}`); console.log(`šŸ“ Definition: ${focusVirtue.definition}`); console.log(`šŸŽÆ Goals: ${typeof goals === 'string' ? goals : 'Not specified'}`); console.log(`šŸ“… Daily Practices: ${typeof dailyPractices === 'string' ? dailyPractices : 'Not specified'}`); console.log(`šŸ“Š Accountability: ${typeof accountability === 'string' ? accountability : 'Not specified'}`); // Save plan const savePlan = await confirm({ message: 'Save this plan to your monthly records?', initialValue: true }); if (savePlan && typeof savePlan !== 'symbol') { const interventionPlan = { focusVirtue: focusVirtue.name, goals: typeof goals === 'string' ? [goals] : [], dailyPractices: typeof dailyPractices === 'string' ? [dailyPractices] : [], weeklyReflections: [], monthlyMilestones: [], accountabilityMeasures: typeof accountability === 'string' ? [accountability] : [] }; await this.saveDeepDive(monthlyAnalysis, interventionPlan); } outro(picocolors.green('šŸ“‹ Monthly plan created! Good luck with your focused practice!')); } async generateMonthlyAnalysis() { const currentMonth = new Date().toISOString().slice(0, 7); const monthStart = new Date(currentMonth + '-01'); const rawEntries = this.db.getEntriesForProfile(this.profile.id, monthStart); const entries = this.parseEntries(rawEntries); const virtues = this.db.getVirtuesForProfile(this.profile.id); // Calculate month data const coherences = entries.map(entry => { const scores = {}; entry.virtue_scores.forEach((vs) => { scores[vs.virtue_id] = { want: vs.want_score, pull: vs.pull_score }; }); return { date: entry.date, coherence: calculateCoherence(scores) }; }); const averageCoherence = coherences.length > 0 ? coherences.reduce((sum, val) => sum + val.coherence, 0) / coherences.length : 0; const bestDay = coherences.reduce((best, current) => current.coherence > best.coherence ? current : best, { date: '', coherence: 0 }); const challengingDay = coherences.reduce((worst, current) => current.coherence < worst.coherence ? current : worst, { date: '', coherence: 1 }); // Calculate consistency (how regular the check-ins are) const daysInMonth = new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0).getDate(); const consistencyScore = entries.length / daysInMonth; // Virtue analysis const virtueAnalysis = virtues.map(virtue => { const virtueEntries = entries.filter(entry => entry.virtue_scores.some((vs) => vs.virtue_id === virtue.id)); const alignments = virtueEntries.map(entry => { const score = entry.virtue_scores.find((vs) => vs.virtue_id === virtue.id); return score ? calculateAlignment(score.want_score, score.pull_score) : 0; }).filter(alignment => alignment > 0); const monthlyAverage = alignments.length > 0 ? alignments.reduce((sum, val) => sum + val, 0) / alignments.length : 0; // Simple trend calculation (first half vs second half) const halfPoint = Math.floor(alignments.length / 2); const firstHalf = alignments.slice(0, halfPoint); const secondHalf = alignments.slice(halfPoint); const firstAvg = firstHalf.length > 0 ? firstHalf.reduce((s, v) => s + v, 0) / firstHalf.length : 0; const secondAvg = secondHalf.length > 0 ? secondHalf.reduce((s, v) => s + v, 0) / secondHalf.length : 0; let trend = 'stable'; if (secondAvg > firstAvg + 0.1) trend = 'improving'; else if (secondAvg < firstAvg - 0.1) trend = 'declining'; return { virtue, monthlyAverage, trend, keyInsights: [], growthAreas: [] }; }); return { monthData: { month: currentMonth, totalEntries: entries.length, averageCoherence, bestDay, challengingDay, consistencyScore }, virtueAnalysis, overallThemes: this.identifyThemes(entries, virtues), challengingPatterns: [], strengthPatterns: [] }; } parseEntries(rawEntries) { return rawEntries.map(entry => ({ ...entry, virtue_scores: typeof entry.virtue_scores === 'string' ? JSON.parse(entry.virtue_scores) : entry.virtue_scores })); } identifyThemes(entries, virtues) { const themes = []; if (entries.length >= 15) { themes.push('Consistent practice and commitment'); } // Check for virtue-specific patterns virtues.forEach(virtue => { const virtueEntries = entries.filter(entry => entry.virtue_scores.some((vs) => vs.virtue_id === virtue.id)); if (virtueEntries.length > entries.length * 0.8) { themes.push(`Strong focus on ${virtue.name}`); } }); return themes; } async generateAIInsights(analysis) { const prompt = this.buildMonthlyInsightPrompt(analysis); return await this.claudeService.generateMonthlyIntervention(prompt); } buildMonthlyInsightPrompt(analysis) { return `You are a wise virtue ethics coach analyzing someone's monthly virtue tracking data. MONTHLY SUMMARY: - Profile: ${this.profile.name} (${this.profile.philosophical_template}) - Month: ${analysis.monthData.month} - Total Check-ins: ${analysis.monthData.totalEntries} - Average Coherence: ${(analysis.monthData.averageCoherence * 100).toFixed(1)}% - Consistency: ${(analysis.monthData.consistencyScore * 100).toFixed(1)}% VIRTUE PERFORMANCE: ${analysis.virtueAnalysis.map(va => `- ${va.virtue.name}: ${(va.monthlyAverage * 100).toFixed(1)}% (${va.trend})`).join('\n')} THEMES IDENTIFIED: ${analysis.overallThemes.join('\n')} Provide a comprehensive monthly assessment including: 1. Celebration of growth and achievements 2. Compassionate analysis of challenges 3. Key insights about their virtue development 4. Patterns you notice in their practice 5. Wisdom for continued growth Be encouraging, specific to their data, and philosophically grounded. Use markdown formatting. 300-400 words.`; } async createInterventionPlan(analysis) { // Find the virtue that needs most attention const challengingVirtue = analysis.virtueAnalysis .sort((a, b) => a.monthlyAverage - b.monthlyAverage)[0]; if (this.claudeService.isConfigured()) { const prompt = `Create a focused intervention plan for improving ${challengingVirtue.virtue.name}. Current performance: ${(challengingVirtue.monthlyAverage * 100).toFixed(1)}% Definition: ${challengingVirtue.virtue.definition} Trend: ${challengingVirtue.trend} Create: 1. 3 specific, measurable goals for next month 2. 3 daily practices (5-15 minutes each) 3. 3 weekly reflection questions 4. 3 monthly milestones to track progress 5. 3 accountability measures Format as JSON with arrays for each category.`; try { const aiPlan = await this.claudeService.generateInsights(prompt); // Try to parse JSON response const jsonMatch = aiPlan.match(/\{[\s\S]*\}/); if (jsonMatch) { const parsed = JSON.parse(jsonMatch[0]); return { focusVirtue: challengingVirtue.virtue.name, goals: parsed.goals || [], dailyPractices: parsed.dailyPractices || [], weeklyReflections: parsed.weeklyReflections || [], monthlyMilestones: parsed.monthlyMilestones || [], accountabilityMeasures: parsed.accountabilityMeasures || [] }; } } catch { // Fall back to manual plan creation } } // Fallback intervention plan return { focusVirtue: challengingVirtue.virtue.name, goals: [ `Improve ${challengingVirtue.virtue.name} alignment by 15%`, 'Practice daily reflection on this virtue', 'Apply this virtue in one specific life area' ], dailyPractices: [ '5-minute morning intention setting', 'Evening reflection on virtue practice', 'Mindful application during daily activities' ], weeklyReflections: [ 'How did I embody this virtue this week?', 'What challenges did I face?', 'What progress do I notice?' ], monthlyMilestones: [ 'Week 1: Establish daily practices', 'Week 2: Consistent application', 'Week 3: Deeper integration', 'Week 4: Sustained improvement' ], accountabilityMeasures: [ 'Daily virtue tracking scores', 'Weekly written reflections', 'Monthly coherence improvement' ] }; } displayMonthlyOverview(analysis) { console.log(picocolors.bold('\nšŸ“… Monthly Overview')); console.log('═'.repeat(50)); console.log(`šŸ“Š Check-ins: ${picocolors.cyan(analysis.monthData.totalEntries.toString())}`); console.log(`šŸŽÆ Average Coherence: ${this.formatCoherence(analysis.monthData.averageCoherence)}`); console.log(`⚔ Consistency: ${this.formatCoherence(analysis.monthData.consistencyScore)}`); console.log(`🌟 Best Day: ${formatDateDisplay(new Date(analysis.monthData.bestDay.date))} (${this.formatCoherence(analysis.monthData.bestDay.coherence)})`); console.log(`šŸ’Ŗ Growth Day: ${formatDateDisplay(new Date(analysis.monthData.challengingDay.date))} (${this.formatCoherence(analysis.monthData.challengingDay.coherence)})`); } displayVirtueAnalysis(analysis) { console.log(picocolors.bold('\nšŸŽÆ Virtue Analysis')); console.log('═'.repeat(50)); analysis.virtueAnalysis.forEach(va => { const trendIcon = va.trend === 'improving' ? 'šŸ“ˆ' : va.trend === 'declining' ? 'šŸ“‰' : 'šŸ“Š'; const trendColor = va.trend === 'improving' ? picocolors.green : va.trend === 'declining' ? picocolors.red : picocolors.yellow; console.log(`${va.virtue.emoji} ${va.virtue.name}: ${this.formatCoherence(va.monthlyAverage)} ${trendIcon} ${trendColor(va.trend)}`); }); } displayAIInsights(insights) { console.log(picocolors.bold('\n🧠 AI Insights')); console.log('═'.repeat(50)); console.log(insights); } displayInterventionPlan(plan) { console.log(picocolors.bold(`\nšŸ’Ŗ Intervention Plan: ${plan.focusVirtue}`)); console.log('═'.repeat(50)); console.log(picocolors.bold('\nšŸŽÆ Goals:')); plan.goals.forEach(goal => console.log(`• ${goal}`)); console.log(picocolors.bold('\nšŸ“… Daily Practices:')); plan.dailyPractices.forEach(practice => console.log(`• ${practice}`)); console.log(picocolors.bold('\nšŸ¤” Weekly Reflections:')); plan.weeklyReflections.forEach(reflection => console.log(`• ${reflection}`)); console.log(picocolors.bold('\nšŸ“ˆ Monthly Milestones:')); plan.monthlyMilestones.forEach(milestone => console.log(`• ${milestone}`)); console.log(picocolors.bold('\nšŸ“Š Accountability:')); plan.accountabilityMeasures.forEach(measure => console.log(`• ${measure}`)); } async saveDeepDive(analysis, interventionPlan, reflections) { const deepDiveData = { analysis, interventionPlan, reflections: reflections || [], createdAt: new Date().toISOString() }; this.db.createMonthlyDeepDiveSimple(this.profile.id, analysis.monthData.month, JSON.stringify(deepDiveData), interventionPlan ? JSON.stringify(interventionPlan) : null); // Auto-save if configured await this.autoSaveManager.saveMonthlyDeepDive(this.profile, analysis.monthData.month, deepDiveData); log.success('Monthly assessment saved to your records!'); } formatCoherence(coherence) { const percentage = (coherence * 100).toFixed(1); const color = coherence >= 0.8 ? picocolors.green : coherence >= 0.6 ? picocolors.yellow : picocolors.red; const icon = coherence >= 0.8 ? '🟢' : coherence >= 0.6 ? '🟔' : 'šŸ”“'; return `${color(percentage + '%')} ${icon}`; } } //# sourceMappingURL=MonthlyDeepDive.js.map