UNPKG

faj-cli

Version:

FAJ - A powerful CLI resume builder with AI enhancement and multi-format export

517 lines 22.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExperienceCommand = void 0; const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); const inquirer_1 = __importDefault(require("inquirer")); const Logger_1 = require("../../utils/Logger"); const ConfigManager_1 = require("../../core/config/ConfigManager"); const ExperienceManager_1 = require("../../core/experience/ExperienceManager"); class ExperienceCommand { logger; configManager; experienceManager; constructor() { this.logger = new Logger_1.Logger('ExperienceCommand'); this.configManager = ConfigManager_1.ConfigManager.getInstance(); this.experienceManager = ExperienceManager_1.ExperienceManager.getInstance(); } register(program) { const experience = program .command('experience') .alias('exp') .description('Manage your work experience'); experience .command('add') .description('Add a new work experience') .option('-t, --template <type>', 'Use a template (internet/finance/gaming/startup/enterprise)') .option('-e, --example', 'Show an example before starting') .action(async (options) => { try { await this.add(options); } catch (error) { this.logger.error('Failed to add experience', error); process.exit(1); } }); experience .command('list') .description('List all work experiences') .option('-d, --detailed', 'Show detailed information') .action(async (options) => { try { await this.list(options); } catch (error) { this.logger.error('Failed to list experiences', error); process.exit(1); } }); experience .command('edit <id>') .description('Edit an existing work experience') .action(async (id) => { try { await this.edit(id); } catch (error) { this.logger.error('Failed to edit experience', error); process.exit(1); } }); experience .command('delete <id>') .description('Delete a work experience') .action(async (id) => { try { await this.delete(id); } catch (error) { this.logger.error('Failed to delete experience', error); process.exit(1); } }); experience .command('polish <id>') .description('Polish experience description with AI') .action(async (id) => { try { await this.polish(id); } catch (error) { this.logger.error('Failed to polish experience', error); process.exit(1); } }); } async add(options) { // Check if user is configured const profile = await this.configManager.get('profile'); if (!profile || profile.role !== 'developer') { console.log(chalk_1.default.yellow('\n⚠ Developer profile not configured.')); console.log('Please run ' + chalk_1.default.cyan('faj init') + ' first.'); return; } await this.experienceManager.load(); console.log(chalk_1.default.cyan('\n📝 Add Work Experience\n')); // Show example if requested if (options.example) { console.log(chalk_1.default.bold('Example:')); console.log(chalk_1.default.gray('─'.repeat(50))); console.log(this.experienceManager.getExperienceExample()); console.log(chalk_1.default.gray('─'.repeat(50))); console.log(); } // Show template if requested if (options.template) { const templates = this.experienceManager.getExperienceTemplates(); const template = templates[options.template]; if (template) { console.log(chalk_1.default.bold(`Template: ${options.template}`)); console.log(chalk_1.default.gray('─'.repeat(50))); console.log(chalk_1.default.gray(template)); console.log(chalk_1.default.gray('─'.repeat(50))); console.log(chalk_1.default.yellow('\n请根据模板填写您的实际经历:\n')); } } // Collect basic information const basicInfo = await inquirer_1.default.prompt([ { type: 'input', name: 'company', message: 'Company name:', validate: (input) => input.length > 0 || 'Company name is required' }, { type: 'input', name: 'title', message: 'Position/Title:', validate: (input) => input.length > 0 || 'Position is required' }, { type: 'input', name: 'startDate', message: 'Start date (e.g., 2020-01):', validate: (input) => { const pattern = /^\d{4}-\d{2}$/; return pattern.test(input) || 'Please use format: YYYY-MM'; } }, { type: 'confirm', name: 'current', message: 'Is this your current position?', default: false } ]); let endDate; if (!basicInfo.current) { const { end } = await inquirer_1.default.prompt([ { type: 'input', name: 'end', message: 'End date (e.g., 2023-12):', validate: (input) => { const pattern = /^\d{4}-\d{2}$/; return pattern.test(input) || 'Please use format: YYYY-MM'; } } ]); endDate = end; } // Collect description console.log(chalk_1.default.cyan('\n描述您的工作经历:')); console.log(chalk_1.default.gray('提示:包含具体职责、成就、团队规模、使用的技术等')); const { description } = await inquirer_1.default.prompt([ { type: 'input', name: 'description', message: 'Describe your experience:', validate: (input) => input.length > 50 || 'Please provide at least 50 characters' } ]); // Ask about technologies first const { technologies } = await inquirer_1.default.prompt([ { type: 'input', name: 'technologies', message: 'Technologies used (comma-separated):' } ]); // Now ask if they want to enhance with target job console.log(chalk_1.default.cyan('\n🎯 AI Enhancement Option')); console.log(chalk_1.default.gray('You can provide a target job description to optimize your experience.')); const { enhanceWithJob } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'enhanceWithJob', message: 'Would you like to optimize this experience for a specific job?', default: true } ]); let jobDescription = ''; if (enhanceWithJob) { console.log(chalk_1.default.yellow('\nProvide the target job description:')); console.log(chalk_1.default.gray('Paste the job description and type "END" on a new line when done.\n')); const lines = []; const readline = (await Promise.resolve().then(() => __importStar(require('readline')))).createInterface({ input: process.stdin, output: process.stdout }); await new Promise((resolve) => { readline.on('line', (line) => { if (line.trim().toUpperCase() === 'END') { readline.close(); resolve(); } else { lines.push(line); } }); }); jobDescription = lines.join('\n'); if (!jobDescription.trim()) { console.log(chalk_1.default.yellow('No job description provided. Will use general enhancement.')); } } // Create initial experience object const experience = { title: basicInfo.title, company: basicInfo.company, startDate: basicInfo.startDate, endDate: endDate, current: basicInfo.current, description: description, rawDescription: description, highlights: [], technologies: technologies ? technologies.split(',').map((t) => t.trim()).filter(Boolean) : [] }; const spinner = (0, ora_1.default)('Saving experience...').start(); try { const saved = await this.experienceManager.add(experience); spinner.succeed('Experience added successfully!'); // Now enhance with AI if user provided JD or wants general polish if (jobDescription || enhanceWithJob) { spinner.start('Enhancing experience with AI...'); let polished; if (jobDescription.trim()) { // Enhance with job description context polished = await this.experienceManager.polishWithJob(saved.id, description, jobDescription); } else { // Ask if they still want general polish spinner.stop(); const { doGeneralPolish } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'doGeneralPolish', message: 'Would you like AI to polish this experience generally?', default: true } ]); if (doGeneralPolish) { spinner.start('Polishing experience...'); polished = await this.experienceManager.polish(saved.id, description); } } if (polished) { spinner.succeed(jobDescription ? 'Experience optimized for target job!' : 'Experience enhanced successfully!'); console.log(chalk_1.default.cyan('\n✨ AI-Enhanced Description:\n')); console.log(polished.description); if (polished.highlights && polished.highlights.length > 0) { console.log(chalk_1.default.cyan('\n📊 Key Achievements:\n')); polished.highlights.forEach((h, i) => { console.log(`${i + 1}. ${h}`); }); } if (polished.technologies && polished.technologies.length > 0) { console.log(chalk_1.default.cyan('\n💻 Technologies:\n')); console.log(polished.technologies.join(', ')); } if (jobDescription) { console.log(chalk_1.default.green('\n✓ Your experience has been optimized for the target position')); console.log(chalk_1.default.gray('The AI emphasized relevant skills and achievements from your description.')); } } } console.log(chalk_1.default.green(`\n✓ Experience ID: ${saved.id}`)); console.log(chalk_1.default.gray('Use this ID to edit or delete this experience')); } catch (error) { spinner.fail('Failed to save experience'); throw error; } } async list(options) { await this.experienceManager.load(); const experiences = await this.experienceManager.getAll(); if (experiences.length === 0) { console.log(chalk_1.default.yellow('\n📂 No work experiences found.')); console.log('Add your first experience with: ' + chalk_1.default.cyan('faj experience add')); return; } console.log(chalk_1.default.cyan(`\n📋 Work Experiences (${experiences.length} total)\n`)); for (const exp of experiences) { console.log(chalk_1.default.bold(`${exp.title} at ${exp.company}`)); console.log(chalk_1.default.gray(`ID: ${exp.id}`)); console.log(`📅 ${exp.startDate} - ${exp.current ? 'Present' : exp.endDate}`); if (options.detailed) { console.log(`\n${exp.description}\n`); if (exp.highlights.length > 0) { console.log(chalk_1.default.gray('Key Achievements:')); exp.highlights.forEach(h => console.log(` • ${h}`)); } if (exp.technologies.length > 0) { console.log(chalk_1.default.gray(`Technologies: ${exp.technologies.join(', ')}`)); } console.log(chalk_1.default.gray(`Polished: ${exp.polished ? 'Yes ✓' : 'No'}`)); } console.log(chalk_1.default.gray('─'.repeat(50))); } } async edit(id) { await this.experienceManager.load(); const experience = await this.experienceManager.get(id); if (!experience) { console.log(chalk_1.default.red(`\n❌ Experience with ID '${id}' not found.`)); console.log('Use ' + chalk_1.default.cyan('faj experience list') + ' to see all experiences.'); return; } console.log(chalk_1.default.cyan('\n✏️ Edit Work Experience\n')); console.log(chalk_1.default.gray(`Current: ${experience.title} at ${experience.company}`)); const { field } = await inquirer_1.default.prompt([ { type: 'list', name: 'field', message: 'What would you like to edit?', choices: [ { name: 'Title', value: 'title' }, { name: 'Company', value: 'company' }, { name: 'Start Date', value: 'startDate' }, { name: 'End Date', value: 'endDate' }, { name: 'Description', value: 'description' }, { name: 'Achievements', value: 'highlights' }, { name: 'Technologies', value: 'technologies' }, { name: 'Cancel', value: 'cancel' } ] } ]); if (field === 'cancel') { console.log(chalk_1.default.gray('Edit cancelled')); return; } let update = {}; switch (field) { case 'title': case 'company': case 'startDate': case 'endDate': case 'description': const { value } = await inquirer_1.default.prompt([ { type: field === 'description' ? 'editor' : 'input', name: 'value', message: `New ${field}:`, default: experience[field] } ]); update[field] = value; break; case 'highlights': const { highlights } = await inquirer_1.default.prompt([ { type: 'input', name: 'highlights', message: 'Edit achievements (one per line):', default: experience.highlights.join('\n') } ]); update.highlights = highlights.split('\n').filter(Boolean); break; case 'technologies': const { techs } = await inquirer_1.default.prompt([ { type: 'input', name: 'techs', message: 'Technologies (comma-separated):', default: experience.technologies.join(', ') } ]); update.technologies = techs.split(',').map((t) => t.trim()).filter(Boolean); break; } const spinner = (0, ora_1.default)('Updating experience...').start(); try { await this.experienceManager.update(id, update); spinner.succeed('Experience updated successfully!'); } catch (error) { spinner.fail('Failed to update experience'); throw error; } } async delete(id) { await this.experienceManager.load(); const experience = await this.experienceManager.get(id); if (!experience) { console.log(chalk_1.default.red(`\n❌ Experience with ID '${id}' not found.`)); return; } console.log(chalk_1.default.yellow('\n⚠️ Delete Work Experience')); console.log(`${experience.title} at ${experience.company}`); const { confirm } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'confirm', message: 'Are you sure you want to delete this experience?', default: false } ]); if (!confirm) { console.log(chalk_1.default.gray('Deletion cancelled')); return; } const spinner = (0, ora_1.default)('Deleting experience...').start(); try { await this.experienceManager.delete(id); spinner.succeed('Experience deleted successfully!'); } catch (error) { spinner.fail('Failed to delete experience'); throw error; } } async polish(id) { await this.experienceManager.load(); const experience = await this.experienceManager.get(id); if (!experience) { console.log(chalk_1.default.red(`\n❌ Experience with ID '${id}' not found.`)); return; } console.log(chalk_1.default.cyan('\n✨ Polish Experience with AI\n')); console.log(`${experience.title} at ${experience.company}`); let description = experience.rawDescription || experience.description; // Allow user to edit description before polishing const { editFirst } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'editFirst', message: 'Would you like to edit the description before polishing?', default: false } ]); if (editFirst) { const { newDesc } = await inquirer_1.default.prompt([ { type: 'input', name: 'newDesc', message: 'Edit description:', default: description } ]); description = newDesc; } const spinner = (0, ora_1.default)('Polishing with AI...').start(); try { const polished = await this.experienceManager.polish(id, description); if (polished) { spinner.succeed('Experience polished successfully!'); console.log(chalk_1.default.cyan('\n📝 Polished Description:\n')); console.log(polished.description); if (polished.highlights.length > 0) { console.log(chalk_1.default.cyan('\n📊 Key Achievements:\n')); polished.highlights.forEach((h, i) => { console.log(`${i + 1}. ${h}`); }); } if (polished.technologies.length > 0) { console.log(chalk_1.default.cyan('\n🔧 Technologies:\n')); console.log(polished.technologies.join(', ')); } } } catch (error) { spinner.fail('Failed to polish experience'); throw error; } } } exports.ExperienceCommand = ExperienceCommand; //# sourceMappingURL=experience.js.map