virtue-cli
Version:
Personal character development CLI tool for tracking virtues and philosophical alignment
161 lines • 5.94 kB
JavaScript
import { log } from '@clack/prompts';
import { writeFileSync, readFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
export class DiscoverySessionManager {
sessionsDir;
constructor() {
this.sessionsDir = join(homedir(), '.virtue-cli', 'discovery-sessions');
this.ensureSessionsDirectory();
}
ensureSessionsDirectory() {
if (!existsSync(this.sessionsDir)) {
mkdirSync(this.sessionsDir, { recursive: true });
}
}
saveSession(session) {
try {
const sessionPath = join(this.sessionsDir, `${session.id}.json`);
const sessionData = {
...session,
lastSaved: new Date()
};
writeFileSync(sessionPath, JSON.stringify(sessionData, null, 2));
}
catch (error) {
log.warning(`Failed to save discovery session: ${error}`);
}
}
loadSession(sessionId) {
try {
const sessionPath = join(this.sessionsDir, `${sessionId}.json`);
if (!existsSync(sessionPath)) {
return null;
}
const sessionData = JSON.parse(readFileSync(sessionPath, 'utf-8'));
return {
...sessionData,
lastSaved: new Date(sessionData.lastSaved)
};
}
catch (error) {
log.warning(`Failed to load discovery session: ${error}`);
return null;
}
}
listActiveSessions() {
try {
const sessionFiles = readdirSync(this.sessionsDir)
.filter((file) => file.endsWith('.json'));
return sessionFiles
.map((file) => {
try {
const sessionData = JSON.parse(readFileSync(join(this.sessionsDir, file), 'utf-8'));
return {
...sessionData,
lastSaved: new Date(sessionData.lastSaved)
};
}
catch {
return null;
}
})
.filter((session) => session && !session.isComplete)
.sort((a, b) => b.lastSaved.getTime() - a.lastSaved.getTime());
}
catch (error) {
log.warning(`Failed to list discovery sessions: ${error}`);
return [];
}
}
deleteSession(sessionId) {
try {
const sessionPath = join(this.sessionsDir, `${sessionId}.json`);
if (existsSync(sessionPath)) {
unlinkSync(sessionPath);
}
}
catch (error) {
log.warning(`Failed to delete discovery session: ${error}`);
}
}
calculateProgress(session) {
const totalPhases = 3;
const baseQuestions = 4; // Initial questions before AI discovery
const maxAIQuestions = 21; // Maximum additional AI questions (25 total - 4 base)
const totalQuestions = baseQuestions + maxAIQuestions;
const completedQuestions = session.currentQuestionCount;
const percentComplete = Math.min((completedQuestions / totalQuestions) * 100, 100);
// Estimate remaining time based on average 2 minutes per question
const remainingQuestions = Math.max(0, totalQuestions - completedQuestions);
const estimatedTimeRemaining = remainingQuestions * 2;
return {
totalQuestions,
completedQuestions,
currentPhase: session.currentPhase,
totalPhases,
percentComplete: Math.round(percentComplete),
estimatedTimeRemaining,
canPause: !session.isComplete && completedQuestions > 0,
canResume: !session.isComplete && completedQuestions > 0
};
}
createSession(profileName) {
const sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
return {
id: sessionId,
profileName,
responses: [],
currentPhase: 1,
currentQuestionCount: 0,
lastSaved: new Date(),
isComplete: false
};
}
updateSession(session, newResponses, currentPhase, questionCount) {
const updatedSession = {
...session,
responses: [...session.responses, ...newResponses],
currentPhase,
currentQuestionCount: questionCount,
lastSaved: new Date()
};
this.saveSession(updatedSession);
return updatedSession;
}
completeSession(session) {
const completedSession = {
...session,
isComplete: true,
lastSaved: new Date()
};
this.saveSession(completedSession);
// Clean up completed sessions older than 30 days
this.cleanupOldSessions();
}
cleanupOldSessions() {
try {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const sessionFiles = readdirSync(this.sessionsDir)
.filter((file) => file.endsWith('.json'));
sessionFiles.forEach((file) => {
try {
const sessionPath = join(this.sessionsDir, file);
const sessionData = JSON.parse(readFileSync(sessionPath, 'utf-8'));
const lastSaved = new Date(sessionData.lastSaved);
if (sessionData.isComplete && lastSaved < thirtyDaysAgo) {
unlinkSync(sessionPath);
}
}
catch {
// Ignore errors for individual files
}
});
}
catch {
// Silently handle cleanup errors
}
}
}
//# sourceMappingURL=DiscoverySessionManager.js.map