UNPKG

faj-cli

Version:

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

437 lines • 18.7 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.ResumeCommand = 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 ResumeManager_1 = require("../../core/resume/ResumeManager"); class ResumeCommand { logger; resumeManager; constructor() { this.logger = new Logger_1.Logger('ResumeCommand'); this.resumeManager = ResumeManager_1.ResumeManager.getInstance(); } register(program) { const resume = program .command('resume') .description('Manage your resume'); resume .command('show') .description('Display current resume') .option('-f, --format <format>', 'Display format (text, json, md)', 'text') .action(async (options) => { try { await this.show(options); } catch (error) { this.logger.error('Failed to show resume', error); process.exit(1); } }); resume .command('update') .description('Update resume with AI') .action(async () => { try { await this.update(); } catch (error) { this.logger.error('Failed to update resume', error); process.exit(1); } }); resume .command('regenerate') .description('Regenerate resume from scratch') .action(async () => { try { await this.regenerate(); } catch (error) { this.logger.error('Failed to regenerate resume', error); process.exit(1); } }); resume .command('export <format>') .description('Export resume (json, md, html)') .option('-o, --output <file>', 'Output file name') .action(async (format, options) => { try { await this.export(format, options); } catch (error) { this.logger.error('Failed to export resume', error); process.exit(1); } }); resume .command('tailor') .description('Tailor resume to a specific job description') .option('-j, --job <file>', 'Path to job description file') .option('-d, --description <text>', 'Job description text') .action(async (options) => { try { await this.tailor(options); } catch (error) { this.logger.error('Failed to tailor resume', error); process.exit(1); } }); } async show(options) { const resume = await this.resumeManager.get(); if (!resume) { console.log(chalk_1.default.yellow('\n⚠ No resume found.')); console.log('Generate one with: ' + chalk_1.default.cyan('faj analyze <project-path>')); return; } if (options.format === 'json') { console.log(JSON.stringify(resume, null, 2)); return; } if (options.format === 'md') { const markdown = await this.resumeManager.export('md'); console.log(markdown); return; } // Text format console.log(chalk_1.default.cyan('\nšŸ“„ Your Resume\n')); console.log(chalk_1.default.gray('─'.repeat(50))); // Basic Information if (resume.basicInfo) { console.log(chalk_1.default.bold('\nBasic Information:')); console.log(` Name: ${resume.basicInfo.name}`); console.log(` Email: ${resume.basicInfo.email}`); if (resume.basicInfo.phone) { console.log(` Phone: ${resume.basicInfo.phone}`); } if (resume.basicInfo.location) { console.log(` Location: ${resume.basicInfo.location}`); } if (resume.basicInfo.languages && resume.basicInfo.languages.length > 0) { console.log(` Languages: ${resume.basicInfo.languages.join(', ')}`); } if (resume.basicInfo.githubUrl) { console.log(` GitHub: ${chalk_1.default.cyan(resume.basicInfo.githubUrl)}`); } if (resume.basicInfo.linkedinUrl) { console.log(` LinkedIn: ${chalk_1.default.cyan(resume.basicInfo.linkedinUrl)}`); } if (resume.basicInfo.portfolioUrl) { console.log(` Portfolio: ${chalk_1.default.cyan(resume.basicInfo.portfolioUrl)}`); } } // Professional Summary removed // Work Experience (moved to second position after basic info) if (resume.content.experience.length > 0) { console.log(chalk_1.default.bold('\nWork Experience:')); for (const exp of resume.content.experience.slice(0, 2)) { console.log(chalk_1.default.cyan(` ${exp.title}${exp.company ? ' at ' + exp.company : ''}`)); console.log(chalk_1.default.gray(` ${exp.startDate} - ${exp.current ? 'Present' : exp.endDate}`)); console.log(` ${exp.description}`); if (exp.highlights && exp.highlights.length > 0) { console.log(chalk_1.default.gray(' Key Achievements:')); for (const highlight of exp.highlights.slice(0, 3)) { console.log(` • ${highlight}`); } } if (exp.technologies && exp.technologies.length > 0) { console.log(chalk_1.default.gray(` Technologies: ${exp.technologies.slice(0, 5).join(', ')}`)); } } if (resume.content.experience.length > 2) { console.log(chalk_1.default.gray(` ... and ${resume.content.experience.length - 2} more positions`)); } } // Project Experience (moved to third position) if (resume.content.projects.length > 0) { console.log(chalk_1.default.bold('\nProject Experience:')); for (const project of resume.content.projects.slice(0, 3)) { console.log(chalk_1.default.cyan(` ${project.name}`)); console.log(` ${project.description}`); if (project.highlights && project.highlights.length > 0) { for (const highlight of project.highlights.slice(0, 2)) { console.log(` • ${highlight}`); } } if (project.technologies.length > 0) { console.log(chalk_1.default.gray(` Tech Stack: ${project.technologies.slice(0, 5).join(', ')}`)); } } if (resume.content.projects.length > 3) { console.log(chalk_1.default.gray(` ... and ${resume.content.projects.length - 3} more projects`)); } } // Technical Skills (moved to fourth position) if (resume.content.skills.length > 0) { console.log(chalk_1.default.bold('\nTechnical Skills:')); const skillsByCategory = {}; for (const skill of resume.content.skills) { const category = skill.category || 'other'; if (!skillsByCategory[category]) { skillsByCategory[category] = []; } skillsByCategory[category].push(`${skill.name} (${skill.level})`); } for (const [category, skills] of Object.entries(skillsByCategory)) { const categoryLabel = this.getCategoryLabel(category); console.log(chalk_1.default.gray(` ${categoryLabel}:`)); console.log(` ${skills.join(', ')}`); } } // Metadata console.log(chalk_1.default.gray('\n─'.repeat(50))); console.log(chalk_1.default.gray(`Version: ${resume.version} | AI Provider: ${resume.aiProvider}`)); console.log(chalk_1.default.gray(`Published: ${resume.metadata.published ? 'Yes' : 'No'}`)); if (resume.metadata.ipfsHash) { console.log(chalk_1.default.gray(`IPFS Hash: ${resume.metadata.ipfsHash}`)); } } async update() { const resume = await this.resumeManager.get(); if (!resume) { console.log(chalk_1.default.yellow('\n⚠ No resume found.')); console.log('Generate one with: ' + chalk_1.default.cyan('faj analyze <project-path>')); return; } console.log(chalk_1.default.cyan('\nšŸ“ Update Resume\n')); const { updateType } = await inquirer_1.default.prompt([ { type: 'list', name: 'updateType', message: 'What would you like to update?', choices: [ { name: 'Summary', value: 'summary' }, { name: 'Skills', value: 'skills' }, { name: 'Add new project', value: 'project' }, { name: 'Add experience', value: 'experience' }, { name: 'Education', value: 'education' }, { name: 'Regenerate with AI', value: 'regenerate' }, ], }, ]); if (updateType === 'regenerate') { await this.regenerate(); return; } if (updateType === 'summary') { const { summary } = await inquirer_1.default.prompt([ { type: 'editor', name: 'summary', message: 'Enter your professional summary:', default: resume.content.summary, }, ]); const spinner = (0, ora_1.default)('Updating resume...').start(); try { await this.resumeManager.update({ content: { ...resume.content, summary }, }); spinner.succeed('Resume updated successfully!'); } catch (error) { spinner.fail('Failed to update resume'); throw error; } } // Other update types would follow similar pattern console.log(chalk_1.default.yellow('\nFull update functionality coming soon!')); } getCategoryLabel(category) { const labels = { 'language': 'Programming Languages', 'framework': 'Frameworks', 'tool': 'Tools & Technologies', 'database': 'Databases', 'other': 'Other Skills' }; return labels[category] || category; } async regenerate() { const { confirm } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'confirm', message: 'This will regenerate your entire resume. Continue?', default: false, }, ]); if (!confirm) { console.log(chalk_1.default.yellow('Regeneration cancelled.')); return; } console.log(chalk_1.default.yellow('\nTo regenerate, analyze your projects again:')); console.log(chalk_1.default.cyan(' faj analyze <project-paths>')); } async tailor(options) { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); // Get job description from file or direct input let jobDescription = ''; if (options.job) { // Read from file try { jobDescription = await fs.readFile(options.job, 'utf-8'); } catch (error) { this.logger.error(`Failed to read job description file: ${options.job}`); process.exit(1); } } else if (options.description) { jobDescription = options.description; } else { // Interactive prompt for job description const { inputMethod } = await inquirer_1.default.prompt([ { type: 'list', name: 'inputMethod', message: 'How would you like to provide the job description?', choices: [ { name: 'Paste text', value: 'text' }, { name: 'From file', value: 'file' }, ], }, ]); if (inputMethod === 'file') { const { filePath } = await inquirer_1.default.prompt([ { type: 'input', name: 'filePath', message: 'Enter the path to the job description file:', validate: async (input) => { try { await fs.access(input); return true; } catch { return 'File not found. Please enter a valid file path.'; } }, }, ]); jobDescription = await fs.readFile(filePath, 'utf-8'); } else { const { description } = await inquirer_1.default.prompt([ { type: 'editor', name: 'description', message: 'Paste the job description (save and close editor when done):', }, ]); jobDescription = description; } } if (!jobDescription.trim()) { this.logger.error('Job description cannot be empty'); process.exit(1); } const spinner = (0, ora_1.default)('Tailoring resume to job description...').start(); try { await this.resumeManager.tailorToJob(jobDescription); spinner.succeed('Resume tailored successfully!'); console.log(chalk_1.default.green('\nāœ“ Resume has been tailored to the job description')); console.log(chalk_1.default.gray('Your experiences and projects have been optimized to match the job requirements.')); // Ask if user wants to export const { shouldExport } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'shouldExport', message: 'Would you like to export the tailored resume?', default: true, }, ]); if (shouldExport) { const { exportFormat } = await inquirer_1.default.prompt([ { type: 'list', name: 'exportFormat', message: 'Choose export format:', choices: [ { name: 'PDF', value: 'pdf' }, { name: 'HTML', value: 'html' }, { name: 'Markdown', value: 'md' }, { name: 'JSON', value: 'json' }, ], }, ]); const outputPath = await this.resumeManager.export(exportFormat); console.log(chalk_1.default.green(`\nāœ“ Tailored resume exported to: ${outputPath}`)); } } catch (error) { spinner.fail('Failed to tailor resume'); throw error; } } async export(format, options) { const validFormats = ['json', 'md', 'html']; if (!validFormats.includes(format)) { console.log(chalk_1.default.red(`āœ— Invalid format: ${format}`)); console.log(`Valid formats: ${validFormats.join(', ')}`); return; } const resume = await this.resumeManager.get(); if (!resume) { console.log(chalk_1.default.yellow('\n⚠ No resume found.')); console.log('Generate one with: ' + chalk_1.default.cyan('faj analyze <project-path>')); return; } const spinner = (0, ora_1.default)(`Exporting resume as ${format}...`).start(); try { const exported = await this.resumeManager.export(format); const filename = options.output || `resume.${format}`; const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); await fs.writeFile(filename, exported, 'utf-8'); spinner.succeed(`Resume exported to ${filename}`); } catch (error) { spinner.fail(`Failed to export resume: ${error.message}`); throw error; } } } exports.ResumeCommand = ResumeCommand; //# sourceMappingURL=resume.js.map