@hanzo/persona 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const readline = require('readline');
6
+
7
+ const rl = readline.createInterface({
8
+ input: process.stdin,
9
+ output: process.stdout
10
+ });
11
+
12
+ // Helper to ask questions
13
+ const question = (q) => new Promise(resolve => rl.question(q, resolve));
14
+
15
+ // Categories and their directories
16
+ const CATEGORIES = {
17
+ programmer: 'programmers',
18
+ philosopher: 'philosophers',
19
+ scientist: 'scientists',
20
+ religious: 'religious',
21
+ revolutionary: 'revolutionaries',
22
+ writer: 'writers',
23
+ artist: 'artists',
24
+ architect: 'architects',
25
+ athlete: 'athletes',
26
+ explorer: 'explorers',
27
+ activist: 'activists',
28
+ tech_leader: 'leaders',
29
+ leader: 'leaders',
30
+ pioneer: 'pioneers',
31
+ special: 'special'
32
+ };
33
+
34
+ // Generate ID from name
35
+ function generateId(name) {
36
+ return name
37
+ .toLowerCase()
38
+ .replace(/[^a-z0-9\s-]/g, '')
39
+ .replace(/\s+/g, '_')
40
+ .replace(/-+/g, '_')
41
+ .substring(0, 50);
42
+ }
43
+
44
+ // Main function
45
+ async function addPersonality() {
46
+ console.log('\n=== Add New Personality ===\n');
47
+
48
+ try {
49
+ // Basic info
50
+ const name = await question('Name (display name): ');
51
+ const fullName = await question('Full name (optional, press Enter to skip): ');
52
+ const id = await question(`ID (press Enter for "${generateId(name)}"): `) || generateId(name);
53
+
54
+ // Category
55
+ console.log('\nCategories:', Object.keys(CATEGORIES).join(', '));
56
+ const category = await question('Category: ');
57
+
58
+ if (!CATEGORIES[category]) {
59
+ console.error(`Invalid category: ${category}`);
60
+ process.exit(1);
61
+ }
62
+
63
+ // Tags
64
+ const tagsInput = await question('Tags (comma-separated, optional): ');
65
+ const tags = tagsInput ? tagsInput.split(',').map(t => t.trim()).filter(Boolean) : [];
66
+
67
+ // OCEAN scores
68
+ console.log('\n=== OCEAN Scores (0-100) ===');
69
+ const openness = parseInt(await question('Openness (creativity, curiosity): ')) || 50;
70
+ const conscientiousness = parseInt(await question('Conscientiousness (organization, discipline): ')) || 50;
71
+ const extraversion = parseInt(await question('Extraversion (sociability, energy): ')) || 50;
72
+ const agreeableness = parseInt(await question('Agreeableness (cooperation, trust): ')) || 50;
73
+ const neuroticism = parseInt(await question('Neuroticism (emotional instability): ')) || 50;
74
+
75
+ // Personality
76
+ console.log('\n=== Personality ===');
77
+ const summary = await question('Summary (brief description): ');
78
+ const philosophy = await question('Philosophy (core belief/motto): ');
79
+ const approach = await question('Approach (optional): ');
80
+ const communication = await question('Communication style (optional): ');
81
+
82
+ // Values
83
+ const valuesInput = await question('Core values (comma-separated, optional): ');
84
+ const values = valuesInput ? valuesInput.split(',').map(v => v.trim()).filter(Boolean) : undefined;
85
+
86
+ // Quotes
87
+ console.log('\n=== Quotes (optional, enter empty line to finish) ===');
88
+ const quotes = [];
89
+ let quoteNum = 1;
90
+ while (true) {
91
+ const quote = await question(`Quote ${quoteNum}: `);
92
+ if (!quote) break;
93
+ quotes.push(quote);
94
+ quoteNum++;
95
+ }
96
+
97
+ // For programmers, add technical info
98
+ let technical;
99
+ if (category === 'programmer' || category === 'tech_leader') {
100
+ console.log('\n=== Technical Info (optional) ===');
101
+ const languagesInput = await question('Programming languages (comma-separated): ');
102
+ const languages = languagesInput ? languagesInput.split(',').map(l => l.trim()).filter(Boolean) : undefined;
103
+
104
+ const domainsInput = await question('Domains (comma-separated): ');
105
+ const domains = domainsInput ? domainsInput.split(',').map(d => d.trim()).filter(Boolean) : undefined;
106
+
107
+ const essentialTools = await question('Essential tools (comma-separated): ');
108
+ const essential = essentialTools ? essentialTools.split(',').map(t => t.trim()).filter(Boolean) : undefined;
109
+
110
+ if (languages || domains || essential) {
111
+ technical = {};
112
+ if (languages) technical.languages = languages;
113
+ if (domains) technical.domains = domains;
114
+ if (essential) technical.tools = { essential };
115
+ }
116
+ }
117
+
118
+ // Build the personality object
119
+ const personality = {
120
+ "$schema": "../schemas/personality.schema.json",
121
+ id,
122
+ name,
123
+ category
124
+ };
125
+
126
+ if (fullName) personality.fullName = fullName;
127
+ if (tags.length > 0) personality.tags = tags;
128
+
129
+ personality.ocean = {
130
+ openness: Math.min(100, Math.max(0, openness)),
131
+ conscientiousness: Math.min(100, Math.max(0, conscientiousness)),
132
+ extraversion: Math.min(100, Math.max(0, extraversion)),
133
+ agreeableness: Math.min(100, Math.max(0, agreeableness)),
134
+ neuroticism: Math.min(100, Math.max(0, neuroticism))
135
+ };
136
+
137
+ personality.personality = {
138
+ summary: summary || `${category} personality`,
139
+ philosophy: philosophy || 'No philosophy recorded'
140
+ };
141
+
142
+ if (approach) personality.personality.approach = approach;
143
+ if (communication) personality.personality.communication = communication;
144
+ if (values) personality.personality.values = values;
145
+ if (technical) personality.technical = technical;
146
+ if (quotes.length > 0) personality.quotes = quotes;
147
+
148
+ // Determine target directory
149
+ const targetDir = CATEGORIES[category];
150
+ const baseDir = path.join(__dirname, '..', 'personalities_v2');
151
+ const dirPath = path.join(baseDir, targetDir);
152
+
153
+ // Ensure directory exists
154
+ if (!fs.existsSync(dirPath)) {
155
+ fs.mkdirSync(dirPath, { recursive: true });
156
+ }
157
+
158
+ // Check if file already exists
159
+ const filePath = path.join(dirPath, `${id}.json`);
160
+ if (fs.existsSync(filePath)) {
161
+ const overwrite = await question(`\nFile ${id}.json already exists. Overwrite? (y/N): `);
162
+ if (overwrite.toLowerCase() !== 'y') {
163
+ console.log('Cancelled.');
164
+ process.exit(0);
165
+ }
166
+ }
167
+
168
+ // Write the file
169
+ fs.writeFileSync(filePath, JSON.stringify(personality, null, 2));
170
+ console.log(`\n✅ Created: ${targetDir}/${id}.json`);
171
+
172
+ // Update manifest
173
+ const manifestPath = path.join(baseDir, 'index', 'manifest.json');
174
+ if (fs.existsSync(manifestPath)) {
175
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
176
+
177
+ // Update index
178
+ manifest.index[id] = {
179
+ path: `${targetDir}/${id}.json`,
180
+ name: name,
181
+ category: category,
182
+ tags: tags
183
+ };
184
+
185
+ // Update category
186
+ if (!manifest.categories[category]) {
187
+ manifest.categories[category] = {
188
+ count: 0,
189
+ path: targetDir,
190
+ ids: []
191
+ };
192
+ }
193
+
194
+ if (!manifest.categories[category].ids.includes(id)) {
195
+ manifest.categories[category].ids.push(id);
196
+ manifest.categories[category].count++;
197
+ manifest.total++;
198
+ }
199
+
200
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
201
+ console.log('✅ Updated manifest.json');
202
+ }
203
+
204
+ // Suggest rebuilding
205
+ console.log('\nTo rebuild aggregated files, run: npm run build');
206
+
207
+ } catch (err) {
208
+ console.error('\nError:', err.message);
209
+ process.exit(1);
210
+ } finally {
211
+ rl.close();
212
+ }
213
+ }
214
+
215
+ // Run if called directly
216
+ if (require.main === module) {
217
+ addPersonality();
218
+ }
219
+
220
+ module.exports = { addPersonality };
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ // Configuration
7
+ const PERSONAS_DIR = path.join(__dirname, '..', 'personalities_v2');
8
+ const DIST_DIR = path.join(PERSONAS_DIR, 'dist');
9
+ const INDEX_FILE = path.join(PERSONAS_DIR, 'index', 'manifest.json');
10
+
11
+ // Category mappings (reverse of migrate.js)
12
+ const CATEGORY_DIRS = {
13
+ programmers: ['programmer', 'systems', 'language-creator', 'gaming', 'blockchain'],
14
+ philosophers: ['philosopher'],
15
+ scientists: ['scientist', 'mathematician'],
16
+ religious: ['religious'],
17
+ revolutionaries: ['revolutionary'],
18
+ writers: ['writer', 'poet', 'historian'],
19
+ artists: ['artist', 'musician', 'filmmaker', 'comedian', 'composer'],
20
+ architects: ['architect'],
21
+ athletes: ['athlete'],
22
+ explorers: ['explorer'],
23
+ activists: ['activist'],
24
+ leaders: ['tech_leader', 'leader', 'statesman', 'media'],
25
+ pioneers: ['pioneer'],
26
+ special: ['special', 'master']
27
+ };
28
+
29
+ // Load all personalities from a directory
30
+ function loadPersonalities(dirPath) {
31
+ const personalities = [];
32
+
33
+ if (!fs.existsSync(dirPath)) {
34
+ return personalities;
35
+ }
36
+
37
+ const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.json'));
38
+
39
+ files.forEach(file => {
40
+ try {
41
+ const content = fs.readFileSync(path.join(dirPath, file), 'utf8');
42
+ const personality = JSON.parse(content);
43
+ // Remove the schema reference for the aggregated files
44
+ delete personality.$schema;
45
+ personalities.push(personality);
46
+ } catch (err) {
47
+ console.error(`Error loading ${file}: ${err.message}`);
48
+ }
49
+ });
50
+
51
+ return personalities;
52
+ }
53
+
54
+ // Build all personalities file
55
+ function buildAllPersonalities() {
56
+ console.log('Building all personalities file...');
57
+
58
+ const allPersonalities = [];
59
+ const dirs = fs.readdirSync(PERSONAS_DIR)
60
+ .filter(d => !['dist', 'index', 'schemas', 'scripts'].includes(d))
61
+ .filter(d => fs.statSync(path.join(PERSONAS_DIR, d)).isDirectory());
62
+
63
+ dirs.forEach(dir => {
64
+ const personalities = loadPersonalities(path.join(PERSONAS_DIR, dir));
65
+ allPersonalities.push(...personalities);
66
+ });
67
+
68
+ // Sort by name
69
+ allPersonalities.sort((a, b) => a.name.localeCompare(b.name));
70
+
71
+ // Write the file
72
+ const outputPath = path.join(DIST_DIR, 'all.json');
73
+ fs.writeFileSync(outputPath, JSON.stringify({
74
+ version: '2.0.0',
75
+ generated: new Date().toISOString(),
76
+ total: allPersonalities.length,
77
+ personalities: allPersonalities
78
+ }, null, 2));
79
+
80
+ console.log(` Created: dist/all.json (${allPersonalities.length} personalities)`);
81
+
82
+ return allPersonalities;
83
+ }
84
+
85
+ // Build category-specific files
86
+ function buildCategoryFiles() {
87
+ console.log('\nBuilding category files...');
88
+
89
+ const categoryDir = path.join(DIST_DIR, 'by-category');
90
+ if (!fs.existsSync(categoryDir)) {
91
+ fs.mkdirSync(categoryDir, { recursive: true });
92
+ }
93
+
94
+ Object.entries(CATEGORY_DIRS).forEach(([dirName, categories]) => {
95
+ const dirPath = path.join(PERSONAS_DIR, dirName);
96
+ const personalities = loadPersonalities(dirPath);
97
+
98
+ if (personalities.length > 0) {
99
+ const outputPath = path.join(categoryDir, `${dirName}.json`);
100
+ fs.writeFileSync(outputPath, JSON.stringify({
101
+ version: '2.0.0',
102
+ generated: new Date().toISOString(),
103
+ category: dirName,
104
+ categories: categories,
105
+ total: personalities.length,
106
+ personalities: personalities
107
+ }, null, 2));
108
+
109
+ console.log(` Created: dist/by-category/${dirName}.json (${personalities.length} personalities)`);
110
+ }
111
+ });
112
+ }
113
+
114
+ // Build tag-based collections
115
+ function buildTagFiles(allPersonalities) {
116
+ console.log('\nBuilding tag files...');
117
+
118
+ const tagDir = path.join(DIST_DIR, 'by-tag');
119
+ if (!fs.existsSync(tagDir)) {
120
+ fs.mkdirSync(tagDir, { recursive: true });
121
+ }
122
+
123
+ // Collect all tags
124
+ const tagMap = new Map();
125
+
126
+ allPersonalities.forEach(p => {
127
+ if (p.tags && Array.isArray(p.tags)) {
128
+ p.tags.forEach(tag => {
129
+ if (!tagMap.has(tag)) {
130
+ tagMap.set(tag, []);
131
+ }
132
+ tagMap.get(tag).push(p);
133
+ });
134
+ }
135
+ });
136
+
137
+ // Write files for tags with 3+ personalities
138
+ let tagCount = 0;
139
+ tagMap.forEach((personalities, tag) => {
140
+ if (personalities.length >= 3) {
141
+ const sanitizedTag = tag.replace(/[^a-z0-9_-]/gi, '_');
142
+ const outputPath = path.join(tagDir, `${sanitizedTag}.json`);
143
+
144
+ fs.writeFileSync(outputPath, JSON.stringify({
145
+ version: '2.0.0',
146
+ generated: new Date().toISOString(),
147
+ tag: tag,
148
+ total: personalities.length,
149
+ personalities: personalities
150
+ }, null, 2));
151
+
152
+ tagCount++;
153
+ }
154
+ });
155
+
156
+ console.log(` Created ${tagCount} tag files (tags with 3+ personalities)`);
157
+ }
158
+
159
+ // Generate TypeScript types
160
+ function generateTypes(allPersonalities) {
161
+ console.log('\nGenerating TypeScript types...');
162
+
163
+ const types = `// Auto-generated TypeScript types for personalities
164
+ // Generated: ${new Date().toISOString()}
165
+
166
+ export interface OceanScores {
167
+ openness: number;
168
+ conscientiousness: number;
169
+ extraversion: number;
170
+ agreeableness: number;
171
+ neuroticism: number;
172
+ }
173
+
174
+ export interface PersonalityMetadata {
175
+ born?: string;
176
+ died?: string;
177
+ nationality?: string;
178
+ active?: boolean;
179
+ company?: string;
180
+ achievements?: string[];
181
+ }
182
+
183
+ export interface PersonalityInfo {
184
+ summary: string;
185
+ philosophy: string;
186
+ approach?: string;
187
+ communication?: string;
188
+ values?: string[];
189
+ }
190
+
191
+ export interface TechnicalInfo {
192
+ languages?: string[];
193
+ domains?: string[];
194
+ tools?: {
195
+ essential?: string[];
196
+ preferred?: string[];
197
+ created?: string[];
198
+ };
199
+ }
200
+
201
+ export interface BehavioralTraits {
202
+ codeStyle?: string;
203
+ reviewStyle?: string;
204
+ workStyle?: string;
205
+ collaboration?: string;
206
+ }
207
+
208
+ export interface Personality {
209
+ id: string;
210
+ name: string;
211
+ fullName?: string;
212
+ category: string;
213
+ subcategory?: string;
214
+ tags?: string[];
215
+ metadata?: PersonalityMetadata;
216
+ ocean: OceanScores;
217
+ personality: PersonalityInfo;
218
+ technical?: TechnicalInfo;
219
+ quotes?: string[];
220
+ behavioral?: BehavioralTraits;
221
+ }
222
+
223
+ export interface PersonalityCollection {
224
+ version: string;
225
+ generated: string;
226
+ total: number;
227
+ personalities: Personality[];
228
+ category?: string;
229
+ tag?: string;
230
+ }
231
+
232
+ // Personality ID union type
233
+ export type PersonalityId = ${allPersonalities.map(p => `'${p.id}'`).join(' | ')};
234
+
235
+ // Category union type
236
+ export type PersonalityCategory = ${[...new Set(allPersonalities.map(p => `'${p.category}'`))].join(' | ')};
237
+ `;
238
+
239
+ const outputPath = path.join(DIST_DIR, 'types.ts');
240
+ fs.writeFileSync(outputPath, types);
241
+ console.log(' Created: dist/types.ts');
242
+ }
243
+
244
+ // Generate index file for easy imports
245
+ function generateIndex() {
246
+ console.log('\nGenerating index file...');
247
+
248
+ const indexContent = `// Auto-generated index for personality imports
249
+ // Generated: ${new Date().toISOString()}
250
+
251
+ // Individual personality imports
252
+ ${Object.entries(CATEGORY_DIRS).map(([dir, cats]) => {
253
+ const dirPath = path.join(PERSONAS_DIR, dir);
254
+ if (!fs.existsSync(dirPath)) return '';
255
+
256
+ const files = fs.readdirSync(dirPath).filter(f => f.endsWith('.json'));
257
+ return files.map(f => {
258
+ const id = f.replace('.json', '');
259
+ return `export { default as ${id} } from '../${dir}/${f}';`;
260
+ }).join('\n');
261
+ }).filter(Boolean).join('\n')}
262
+
263
+ // Category collections
264
+ export { default as allPersonalities } from './all.json';
265
+ ${Object.keys(CATEGORY_DIRS).map(dir =>
266
+ `export { default as ${dir} } from './by-category/${dir}.json';`
267
+ ).join('\n')}
268
+
269
+ // Type exports
270
+ export * from './types';
271
+ `;
272
+
273
+ const outputPath = path.join(DIST_DIR, 'index.js');
274
+ fs.writeFileSync(outputPath, indexContent);
275
+ console.log(' Created: dist/index.js');
276
+ }
277
+
278
+ // Update package.json exports
279
+ function updatePackageJson() {
280
+ console.log('\nUpdating package.json exports...');
281
+
282
+ const packagePath = path.join(__dirname, '..', 'package.json');
283
+ if (!fs.existsSync(packagePath)) {
284
+ console.log(' No package.json found, skipping...');
285
+ return;
286
+ }
287
+
288
+ try {
289
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
290
+
291
+ // Add exports field for modern module resolution
292
+ pkg.exports = {
293
+ '.': './personalities_v2/dist/index.js',
294
+ './all': './personalities_v2/dist/all.json',
295
+ './types': './personalities_v2/dist/types.ts',
296
+ './by-category/*': './personalities_v2/dist/by-category/*.json',
297
+ './by-tag/*': './personalities_v2/dist/by-tag/*.json',
298
+ './personalities/*': './personalities_v2/*/*.json'
299
+ };
300
+
301
+ // Add files field to include in npm package
302
+ pkg.files = [
303
+ 'personalities_v2/**/*.json',
304
+ 'personalities_v2/dist/**/*',
305
+ 'personalities_v2/schemas/*.json',
306
+ '!personalities_v2/scripts'
307
+ ];
308
+
309
+ fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2));
310
+ console.log(' Updated package.json with exports field');
311
+ } catch (err) {
312
+ console.error(` Error updating package.json: ${err.message}`);
313
+ }
314
+ }
315
+
316
+ // Main build function
317
+ async function build() {
318
+ console.log('Starting build process...\n');
319
+
320
+ // Ensure dist directories exist
321
+ if (!fs.existsSync(DIST_DIR)) {
322
+ fs.mkdirSync(DIST_DIR, { recursive: true });
323
+ }
324
+
325
+ // Build all files
326
+ const allPersonalities = buildAllPersonalities();
327
+ buildCategoryFiles();
328
+ buildTagFiles(allPersonalities);
329
+ generateTypes(allPersonalities);
330
+ generateIndex();
331
+ updatePackageJson();
332
+
333
+ console.log('\nBuild complete!');
334
+ console.log(`Total personalities: ${allPersonalities.length}`);
335
+ }
336
+
337
+ // Run build
338
+ if (require.main === module) {
339
+ build().catch(err => {
340
+ console.error('Build failed:', err);
341
+ process.exit(1);
342
+ });
343
+ }
344
+
345
+ module.exports = { build };