faj-cli
Version:
FAJ - A powerful CLI resume builder with AI enhancement and multi-format export
517 lines • 22.2 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.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
;