- Core calculations (Life Path, Expression, Soul Urge, Birthday) - Advanced numbers (Maturity, Personality, Hidden Passion, Karmic Lessons) - Timing cycles and optimal days finder - Compatibility analysis and name optimizer - Telos integration for personal development - Professional PDF report generation - Profile management system - Security fix: Add .claude/ to .gitignore 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
153 lines
3.5 KiB
TypeScript
153 lines
3.5 KiB
TypeScript
#!/usr/bin/env bun
|
|
/**
|
|
* Profile Management System
|
|
*
|
|
* Manages numerology profiles stored in ~/.numerology/profiles/
|
|
* Each profile contains name and birthdate for easy reuse across all tools.
|
|
*
|
|
* Usage:
|
|
* import { loadProfile, saveProfile, listProfiles, deleteProfile } from './profile-manager';
|
|
*/
|
|
|
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
|
|
import { join } from 'path';
|
|
import { homedir } from 'os';
|
|
|
|
export interface Profile {
|
|
name: string;
|
|
birthdate: string;
|
|
created: string;
|
|
updated: string;
|
|
}
|
|
|
|
const PROFILES_DIR = join(homedir(), '.numerology', 'profiles');
|
|
|
|
/**
|
|
* Ensure profiles directory exists
|
|
*/
|
|
function ensureProfilesDir(): void {
|
|
if (!existsSync(PROFILES_DIR)) {
|
|
mkdirSync(PROFILES_DIR, { recursive: true });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate profile filename from identifier
|
|
*/
|
|
function getProfilePath(identifier: string): string {
|
|
// Sanitize identifier to create safe filename
|
|
const safeName = identifier.toLowerCase().replace(/[^a-z0-9-_]/g, '-');
|
|
return join(PROFILES_DIR, `${safeName}.json`);
|
|
}
|
|
|
|
/**
|
|
* Save a profile
|
|
*/
|
|
export function saveProfile(identifier: string, name: string, birthdate: string): void {
|
|
ensureProfilesDir();
|
|
|
|
const profilePath = getProfilePath(identifier);
|
|
const now = new Date().toISOString();
|
|
|
|
// Check if profile exists to preserve created date
|
|
let created = now;
|
|
if (existsSync(profilePath)) {
|
|
try {
|
|
const existing = JSON.parse(readFileSync(profilePath, 'utf-8')) as Profile;
|
|
created = existing.created;
|
|
} catch {
|
|
// If file is corrupted, use current time
|
|
}
|
|
}
|
|
|
|
const profile: Profile = {
|
|
name,
|
|
birthdate,
|
|
created,
|
|
updated: now
|
|
};
|
|
|
|
writeFileSync(profilePath, JSON.stringify(profile, null, 2));
|
|
}
|
|
|
|
/**
|
|
* Load a profile
|
|
*/
|
|
export function loadProfile(identifier: string): Profile | null {
|
|
const profilePath = getProfilePath(identifier);
|
|
|
|
if (!existsSync(profilePath)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const data = readFileSync(profilePath, 'utf-8');
|
|
return JSON.parse(data) as Profile;
|
|
} catch (error) {
|
|
console.error(`Error reading profile '${identifier}':`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List all profiles
|
|
*/
|
|
export function listProfiles(): Array<{ identifier: string; profile: Profile }> {
|
|
ensureProfilesDir();
|
|
|
|
const profiles: Array<{ identifier: string; profile: Profile }> = [];
|
|
|
|
try {
|
|
const files = readdirSync(PROFILES_DIR);
|
|
|
|
for (const file of files) {
|
|
if (file.endsWith('.json')) {
|
|
const identifier = file.replace('.json', '');
|
|
const profile = loadProfile(identifier);
|
|
|
|
if (profile) {
|
|
profiles.push({ identifier, profile });
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error listing profiles:', error);
|
|
}
|
|
|
|
return profiles;
|
|
}
|
|
|
|
/**
|
|
* Delete a profile
|
|
*/
|
|
export function deleteProfile(identifier: string): boolean {
|
|
const profilePath = getProfilePath(identifier);
|
|
|
|
if (!existsSync(profilePath)) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
unlinkSync(profilePath);
|
|
return true;
|
|
} catch (error) {
|
|
console.error(`Error deleting profile '${identifier}':`, error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a profile exists
|
|
*/
|
|
export function profileExists(identifier: string): boolean {
|
|
return existsSync(getProfilePath(identifier));
|
|
}
|
|
|
|
/**
|
|
* Get profile identifier from name (for auto-generation)
|
|
*/
|
|
export function generateIdentifier(name: string): string {
|
|
// Generate identifier from first name
|
|
const firstName = name.split(' ')[0];
|
|
return firstName.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
}
|