mirror of
https://github.com/hanzoai/persona.git
synced 2026-10-30 18:24:59 +00:00
* feat: Restructure personalities into individual JSON files - Split mega JSON files into 488 individual personality files - Each personality in its own named JSON file (e.g., zeekay.json, einstein.json) - Created comprehensive index.json with all personality references - Added categories.json for grouping by category - Built JavaScript loader utilities for easy imports - Generated README with complete personality index - Added build and validation scripts for maintenance - More maintainable and reusable structure for JavaScript imports * feat: Enhanced scientist profiles and fixed validation issues - Added detailed fields to 9 major scientist profiles (Einstein, Curie, Darwin, etc.) - Fixed invalid categories for 7 profiles (changed to valid categories) - Added parallel validation script with comprehensive checks - All 487 personality profiles now pass validation - 100% validation success rate across all categories * refactor: Complete personality separation and cleanup - Verified all 487 unique personalities are in individual JSON files - Archived 11 mega personality collection files for safety - Fixed duplicate vaclav_havel entry (was counted twice in mega file) - Profiles directory now contains exactly 487 personality files + 2 index files - Zero duplicate IDs across all individual files - Cleaned personalities directory, keeping only individual files - All personalities properly validated and organized by category * feat: Add 126 personalities for global representation - Added 21 African personalities (Nigeria, Kenya, Ethiopia, Tanzania, etc.) - Added 20 Latin American personalities (Brazil, Argentina, Mexico, Chile, etc.) - Added 25 Asia-Pacific personalities (Indonesia, Thailand, Philippines, Pakistan, etc.) - Added 15 Caribbean personalities (Jamaica, Cuba, Haiti, Trinidad, etc.) - Added 15 Middle Eastern personalities (Turkey, Iran, Israel, Lebanon, etc.) - Added 20 Eastern European personalities (Poland, Czech, Hungary, Romania, etc.) - Added 10 Pacific Island personalities (New Zealand, Australia, Hawaii, Samoa, etc.) Total personalities: 613 (up from 487) Now includes representation from 100+ countries/territories Each personality includes OCEAN scores, philosophy, tools/domains, and origin * feat: enhance 682 personality profiles with comprehensive behavioral modeling - Added 8 major behavioral modeling categories to each profile: - Behavioral traits (values, motivations, fears, strengths, weaknesses, habits, quirks) - Cognitive style (thinking patterns, learning, problem-solving, decision-making) - Social dynamics (interaction, leadership, conflict, collaboration styles) - Communication patterns (verbal style, listening, persuasion, storytelling) - Work methodology (planning, execution, attention, pace, persistence) - Emotional profile (stability, stress response, empathy, self-awareness) - Legacy impact (contributions, influence domains, innovation style) - Category-specific attributes (tailored to each personality type) - Enhanced 682 out of 701 profiles (97.3% success rate) - Added enhancement metadata tracking (date, version, category used) - Created comprehensive Python enhancement script for future updates - Each profile now contains detailed attributes for accurate AI modeling - Fulfills user request for 'as much extra detail as possible to accurately model all qualities and aspects of each persona' --------- Co-authored-by: Hanzo Dev <[email protected]>
384 lines
11 KiB
JavaScript
Executable File
384 lines
11 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Configuration
|
|
const SOURCE_DIR = path.join(__dirname, '..', 'personalities');
|
|
const TARGET_DIR = path.join(__dirname, '..', 'personalities_v2');
|
|
const SCHEMA_PATH = path.join(__dirname, '..', 'schemas', 'personality.schema.json');
|
|
|
|
// Category mappings
|
|
const CATEGORY_DIRS = {
|
|
programmer: 'programmers',
|
|
philosopher: 'philosophers',
|
|
scientist: 'scientists',
|
|
religious: 'religious',
|
|
revolutionary: 'revolutionaries',
|
|
writer: 'writers',
|
|
artist: 'artists',
|
|
musician: 'artists',
|
|
filmmaker: 'artists',
|
|
comedian: 'artists',
|
|
architect: 'architects',
|
|
athlete: 'athletes',
|
|
explorer: 'explorers',
|
|
activist: 'activists',
|
|
tech_leader: 'leaders',
|
|
leader: 'leaders',
|
|
statesman: 'leaders',
|
|
pioneer: 'pioneers',
|
|
mathematician: 'scientists',
|
|
special: 'special',
|
|
systems: 'programmers',
|
|
master: 'programmers',
|
|
'language-creator': 'programmers',
|
|
historian: 'writers',
|
|
gaming: 'programmers',
|
|
blockchain: 'programmers',
|
|
media: 'leaders',
|
|
poet: 'writers'
|
|
};
|
|
|
|
// Initialize directories
|
|
function initDirectories() {
|
|
// Create main directories
|
|
const dirs = [
|
|
TARGET_DIR,
|
|
path.join(TARGET_DIR, 'index'),
|
|
path.join(TARGET_DIR, 'schemas'),
|
|
path.join(TARGET_DIR, 'scripts'),
|
|
path.join(TARGET_DIR, 'dist'),
|
|
path.join(TARGET_DIR, 'dist', 'by-category'),
|
|
path.join(TARGET_DIR, 'dist', 'by-tag')
|
|
];
|
|
|
|
// Create category directories
|
|
const uniqueDirs = [...new Set(Object.values(CATEGORY_DIRS))];
|
|
uniqueDirs.forEach(dir => {
|
|
dirs.push(path.join(TARGET_DIR, dir));
|
|
});
|
|
|
|
dirs.forEach(dir => {
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
console.log(`Created directory: ${dir}`);
|
|
}
|
|
});
|
|
|
|
// Copy schema
|
|
const schemaTarget = path.join(TARGET_DIR, 'schemas', 'personality.schema.json');
|
|
if (fs.existsSync(SCHEMA_PATH) && !fs.existsSync(schemaTarget)) {
|
|
fs.copyFileSync(SCHEMA_PATH, schemaTarget);
|
|
console.log('Copied schema file');
|
|
}
|
|
}
|
|
|
|
// Convert OCEAN scores to 0-100 scale if needed
|
|
function normalizeOcean(ocean) {
|
|
const normalized = {};
|
|
for (const [key, value] of Object.entries(ocean)) {
|
|
// If any value is greater than 100, assume it's on a different scale
|
|
normalized[key] = value > 100 ? Math.round(value / 10) : value;
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
// Generate ID from name
|
|
function generateId(name) {
|
|
return name
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9\s-]/g, '')
|
|
.replace(/\s+/g, '_')
|
|
.replace(/-+/g, '_')
|
|
.substring(0, 50);
|
|
}
|
|
|
|
// Convert old format to new format
|
|
function convertPersonality(oldData, source) {
|
|
const id = oldData.id || generateId(oldData.name);
|
|
const category = oldData.category || 'special';
|
|
|
|
// Build new format
|
|
const newFormat = {
|
|
"$schema": "../schemas/personality.schema.json",
|
|
id: id,
|
|
name: oldData.name || oldData.display_name,
|
|
category: category
|
|
};
|
|
|
|
// Add full name if available
|
|
if (oldData.fullName || oldData.programmer) {
|
|
newFormat.fullName = oldData.fullName || oldData.programmer;
|
|
}
|
|
|
|
// Add subcategory if makes sense
|
|
if (oldData.subcategory) {
|
|
newFormat.subcategory = oldData.subcategory;
|
|
}
|
|
|
|
// Generate tags
|
|
const tags = [];
|
|
if (oldData.tags) {
|
|
tags.push(...oldData.tags);
|
|
} else {
|
|
// Auto-generate some tags
|
|
if (category) tags.push(category);
|
|
if (oldData.primary_tech) tags.push(...oldData.primary_tech.map(t => t.toLowerCase()));
|
|
if (oldData.role) tags.push(...oldData.role.toLowerCase().split(/[&,\s]+/).filter(t => t.length > 2));
|
|
}
|
|
if (tags.length > 0) {
|
|
newFormat.tags = [...new Set(tags)];
|
|
}
|
|
|
|
// Add metadata
|
|
const metadata = {};
|
|
if (oldData.born || oldData.lived) metadata.born = oldData.born || oldData.lived;
|
|
if (oldData.died) metadata.died = oldData.died;
|
|
if (oldData.nationality) metadata.nationality = oldData.nationality;
|
|
if (oldData.company) metadata.company = oldData.company;
|
|
if (oldData.achievements) metadata.achievements = oldData.achievements;
|
|
if (oldData.active !== undefined) metadata.active = oldData.active;
|
|
|
|
if (Object.keys(metadata).length > 0) {
|
|
newFormat.metadata = metadata;
|
|
}
|
|
|
|
// OCEAN scores (normalize to 0-100)
|
|
if (oldData.ocean) {
|
|
newFormat.ocean = normalizeOcean(oldData.ocean);
|
|
} else if (oldData.personality && typeof oldData.personality === 'object' && oldData.personality.openness) {
|
|
newFormat.ocean = normalizeOcean(oldData.personality);
|
|
} else {
|
|
// Default OCEAN scores
|
|
newFormat.ocean = {
|
|
openness: 70,
|
|
conscientiousness: 70,
|
|
extraversion: 50,
|
|
agreeableness: 60,
|
|
neuroticism: 40
|
|
};
|
|
}
|
|
|
|
// Personality info
|
|
const personality = {
|
|
summary: oldData.description || oldData.summary || `${category} personality`,
|
|
philosophy: oldData.philosophy || oldData.quote || "No philosophy recorded"
|
|
};
|
|
|
|
if (oldData.approach) personality.approach = oldData.approach;
|
|
if (oldData.communication) personality.communication = oldData.communication;
|
|
if (oldData.style?.communication) personality.communication = oldData.style.communication;
|
|
if (oldData.values) personality.values = oldData.values;
|
|
if (oldData.principles) personality.values = oldData.principles;
|
|
|
|
newFormat.personality = personality;
|
|
|
|
// Technical info (for programmers)
|
|
if (category === 'programmer' || category === 'tech_leader' || oldData.primary_tech) {
|
|
const technical = {};
|
|
|
|
if (oldData.languages || oldData.primary_tech) {
|
|
technical.languages = oldData.languages || oldData.primary_tech;
|
|
}
|
|
|
|
if (oldData.domains || oldData.tools?.domains) {
|
|
technical.domains = oldData.domains || oldData.tools.domains;
|
|
}
|
|
|
|
if (oldData.tools) {
|
|
technical.tools = {};
|
|
if (oldData.tools.essential) technical.tools.essential = oldData.tools.essential;
|
|
if (oldData.tools.preferred) technical.tools.preferred = oldData.tools.preferred;
|
|
if (oldData.tools.created) technical.tools.created = oldData.tools.created;
|
|
if (oldData.style?.tools) technical.tools.preferred = oldData.style.tools;
|
|
}
|
|
|
|
if (Object.keys(technical).length > 0) {
|
|
newFormat.technical = technical;
|
|
}
|
|
}
|
|
|
|
// Quotes
|
|
if (oldData.quotes) {
|
|
newFormat.quotes = oldData.quotes;
|
|
} else if (oldData.quote) {
|
|
newFormat.quotes = [oldData.quote];
|
|
}
|
|
|
|
// Behavioral traits
|
|
if (oldData.behavioral || oldData.contribution_style || oldData.style) {
|
|
const behavioral = {};
|
|
|
|
if (oldData.behavioral?.codeStyle || oldData.style?.code) {
|
|
behavioral.codeStyle = oldData.behavioral?.codeStyle || oldData.style.code;
|
|
}
|
|
if (oldData.behavioral?.reviewStyle || oldData.contribution_style?.review) {
|
|
behavioral.reviewStyle = oldData.behavioral?.reviewStyle || oldData.contribution_style.review;
|
|
}
|
|
if (oldData.behavioral?.workStyle || oldData.style?.approach) {
|
|
behavioral.workStyle = oldData.behavioral?.workStyle || oldData.style.approach;
|
|
}
|
|
if (oldData.behavioral?.collaboration) {
|
|
behavioral.collaboration = oldData.behavioral.collaboration;
|
|
}
|
|
|
|
if (Object.keys(behavioral).length > 0) {
|
|
newFormat.behavioral = behavioral;
|
|
}
|
|
}
|
|
|
|
return newFormat;
|
|
}
|
|
|
|
// Process a single JSON file
|
|
function processFile(filePath) {
|
|
console.log(`\nProcessing: ${path.basename(filePath)}`);
|
|
|
|
try {
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
const data = JSON.parse(content);
|
|
|
|
let personalities = [];
|
|
|
|
// Handle different formats
|
|
if (data.personalities && Array.isArray(data.personalities)) {
|
|
personalities = data.personalities;
|
|
} else if (data.personas && Array.isArray(data.personas)) {
|
|
personalities = data.personas;
|
|
} else if (data.name) {
|
|
// Single personality file
|
|
personalities = [data];
|
|
} else {
|
|
console.log(` Skipping - unknown format`);
|
|
return 0;
|
|
}
|
|
|
|
let count = 0;
|
|
personalities.forEach(person => {
|
|
try {
|
|
const converted = convertPersonality(person, path.basename(filePath));
|
|
const targetDir = CATEGORY_DIRS[converted.category] || 'special';
|
|
const targetPath = path.join(TARGET_DIR, targetDir, `${converted.id}.json`);
|
|
|
|
// Check for duplicates
|
|
if (fs.existsSync(targetPath)) {
|
|
console.log(` Skipping duplicate: ${converted.id}`);
|
|
return;
|
|
}
|
|
|
|
// Write the file
|
|
fs.writeFileSync(targetPath, JSON.stringify(converted, null, 2));
|
|
console.log(` Created: ${targetDir}/${converted.id}.json`);
|
|
count++;
|
|
} catch (err) {
|
|
console.error(` Error converting ${person.name || 'unknown'}: ${err.message}`);
|
|
}
|
|
});
|
|
|
|
return count;
|
|
} catch (err) {
|
|
console.error(` Error reading file: ${err.message}`);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Generate manifest
|
|
function generateManifest() {
|
|
console.log('\nGenerating manifest...');
|
|
|
|
const manifest = {
|
|
version: "2.0.0",
|
|
generated: new Date().toISOString(),
|
|
total: 0,
|
|
categories: {},
|
|
index: {}
|
|
};
|
|
|
|
// Scan all category directories
|
|
const categoryDirs = [...new Set(Object.values(CATEGORY_DIRS))];
|
|
|
|
categoryDirs.forEach(dir => {
|
|
const dirPath = path.join(TARGET_DIR, dir);
|
|
if (!fs.existsSync(dirPath)) return;
|
|
|
|
const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.json'));
|
|
if (files.length === 0) return;
|
|
|
|
const categoryName = Object.keys(CATEGORY_DIRS).find(key => CATEGORY_DIRS[key] === dir) || dir;
|
|
const ids = [];
|
|
|
|
files.forEach(file => {
|
|
const filePath = path.join(dirPath, file);
|
|
try {
|
|
const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
ids.push(content.id);
|
|
|
|
manifest.index[content.id] = {
|
|
path: path.relative(TARGET_DIR, filePath),
|
|
name: content.name,
|
|
category: content.category,
|
|
tags: content.tags || []
|
|
};
|
|
|
|
manifest.total++;
|
|
} catch (err) {
|
|
console.error(`Error reading ${file}: ${err.message}`);
|
|
}
|
|
});
|
|
|
|
manifest.categories[categoryName] = {
|
|
count: ids.length,
|
|
path: dir,
|
|
ids: ids
|
|
};
|
|
});
|
|
|
|
// Write manifest
|
|
const manifestPath = path.join(TARGET_DIR, 'index', 'manifest.json');
|
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
console.log(`Generated manifest with ${manifest.total} personalities`);
|
|
|
|
return manifest;
|
|
}
|
|
|
|
// Main migration function
|
|
async function migrate() {
|
|
console.log('Starting migration...\n');
|
|
|
|
// Initialize directory structure
|
|
initDirectories();
|
|
|
|
// Process all JSON files
|
|
const files = fs.readdirSync(SOURCE_DIR).filter(f => f.endsWith('.json'));
|
|
let totalConverted = 0;
|
|
|
|
files.forEach(file => {
|
|
const filePath = path.join(SOURCE_DIR, file);
|
|
totalConverted += processFile(filePath);
|
|
});
|
|
|
|
console.log(`\nTotal personalities converted: ${totalConverted}`);
|
|
|
|
// Generate manifest
|
|
const manifest = generateManifest();
|
|
|
|
// Generate category summary
|
|
console.log('\nCategory Summary:');
|
|
Object.entries(manifest.categories).forEach(([cat, info]) => {
|
|
console.log(` ${cat}: ${info.count} personalities`);
|
|
});
|
|
|
|
console.log('\nMigration complete!');
|
|
console.log(`Output directory: ${TARGET_DIR}`);
|
|
}
|
|
|
|
// Run migration
|
|
if (require.main === module) {
|
|
migrate().catch(err => {
|
|
console.error('Migration failed:', err);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
module.exports = { migrate, convertPersonality }; |