virtue-tracker
Version:
Personal character development CLI tool for tracking virtues and philosophical alignment
182 lines (176 loc) • 6.97 kB
JavaScript
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