UNPKG

virtue-tracker

Version:

Personal character development CLI tool for tracking virtues and philosophical alignment

182 lines (176 loc) 6.97 kB
import { log } from '@clack/prompts'; export class ClaudeService { isClaudeCodeEnvironment; constructor() { // Detect if we're running inside Claude Code environment this.isClaudeCodeEnvironment = this.detectClaudeCodeEnvironment(); } detectClaudeCodeEnvironment() { // Check for Claude Code specific environment indicators return !!(process.env.CLAUDE_CODE_SESSION || process.env.CLAUDE_API_URL || process.env.ANTHROPIC_API_KEY || // Check if we have access to Claude Code's internal APIs global.claudeCode); } isConfigured() { return this.isClaudeCodeEnvironment; } async generatePhilosophicalQuestions(initialResponses) { if (!this.isClaudeCodeEnvironment) { throw new Error('Claude Code environment not detected'); } const prompt = `Based on these initial responses about personal values: ${initialResponses.map(r => `- ${r.question}: ${Array.isArray(r.answer) ? r.answer.join(', ') : r.answer}`).join('\n')} Generate 3-4 more insightful questions that will help reveal this person's deeper moral framework and authentic values. The questions should: 1. Be crisp and specific (not abstract) 2. Present concrete scenarios or choices 3. Reveal different aspects of their character 4. Build on their previous answers Format: Return only the questions, one per line.`; try { // Use Claude Code's internal API or subprocess to call Claude const response = await this.callClaudeViaClaudeCode(prompt); return response.trim().split('\n').filter(q => q.trim().length > 0); } catch (error) { log.error('Failed to generate questions: ' + error); return []; } } async deriveVirtuesFromResponses(responses) { if (!this.isClaudeCodeEnvironment) { throw new Error('Claude Code environment not detected'); } const prompt = `Analyze these responses about personal values and derive a personalized virtue framework: ${responses.map(r => `Q: ${r.question}\nA: ${Array.isArray(r.answer) ? r.answer.join(', ') : r.answer}`).join('\n\n')} Based on this person's responses, create 4-6 personalized virtues that: 1. Reflect their authentic values and priorities 2. Are specific and actionable (not generic) 3. Address their growth areas and aspirations 4. Form a coherent framework for character development For each virtue provide: - Name: A clear, memorable name (2-3 words) - Definition: A brief, inspiring definition that resonates with their values (1-2 sentences) - Emoji: A single emoji that represents this virtue Format your response as JSON array: [ { "name": "Virtue Name", "definition": "Brief definition", "emoji": "🎯" } ]`; try { const response = await this.callClaudeViaClaudeCode(prompt); // Extract JSON from response const jsonMatch = response.match(/\[[\s\S]*\]/); if (jsonMatch) { const virtuesData = JSON.parse(jsonMatch[0]); return virtuesData.map((v, index) => ({ name: v.name, definition: v.definition, priority_weight: 1.0 - (index * 0.15), // Decreasing weights template_source: 'ai-derived', emoji: v.emoji || '🎯' })); } // Fallback to default virtues if parsing fails return this.getDefaultVirtues(); } catch (error) { log.error('Failed to derive virtues: ' + error); return this.getDefaultVirtues(); } } getDefaultVirtues() { return [ { name: 'Authentic Growth', definition: 'Continuously evolving while staying true to your core values', priority_weight: 1.0, template_source: 'ai-derived', emoji: '🌱' }, { name: 'Mindful Action', definition: 'Acting with intention and awareness of impact', priority_weight: 0.85, template_source: 'ai-derived', emoji: '🎯' }, { name: 'Compassionate Connection', definition: 'Building meaningful relationships through empathy', priority_weight: 0.7, template_source: 'ai-derived', emoji: '🤝' }, { name: 'Courageous Integrity', definition: 'Standing firm in your principles despite challenges', priority_weight: 0.55, template_source: 'ai-derived', emoji: '💎' } ]; } async callClaudeViaClaudeCode(prompt) { // Strategy 1: Try using subprocess to call Claude Code directly try { const { spawn } = await import('child_process'); // No need for temp file anymore // Call Claude Code CLI with the prompt const process = spawn('claude', ['chat'], { stdio: ['pipe', 'pipe', 'pipe'] }); // Send the prompt directly to stdin process.stdin?.write(prompt); process.stdin?.end(); let output = ''; let error = ''; process.stdout?.on('data', (data) => { output += data.toString(); }); process.stderr?.on('data', (data) => { error += data.toString(); }); return new Promise((resolve, reject) => { process.on('close', (code) => { if (code === 0) { resolve(output); } else { reject(new Error(`Claude Code process failed: ${error}`)); } }); }); } catch (error) { throw new Error(`Failed to call Claude via Claude Code: ${error}`); } } async generateMonthlyIntervention(prompt) { if (!this.isClaudeCodeEnvironment) { throw new Error('Claude Code environment not detected'); } try { return await this.callClaudeViaClaudeCode(prompt); } catch (error) { throw new Error(`Failed to generate monthly intervention: ${error}`); } } async generateInsights(prompt) { if (!this.isClaudeCodeEnvironment) { throw new Error('Claude Code environment not detected'); } try { return await this.callClaudeViaClaudeCode(prompt); } catch (error) { throw new Error(`Failed to generate insights: ${error}`); } } } //# sourceMappingURL=ClaudeService.js.map