faj-cli
Version:
FAJ - A powerful CLI resume builder with AI enhancement and multi-format export
437 lines ⢠18.7 kB
JavaScript
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
;