resuml 2.0.0 → 3.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.
- package/DOCS.md +64 -58
- package/data/skills/emerging.json +59 -11
- package/data/skills/skills.json +14964 -1
- package/dist/chunk-R4MD5YMV.js +17434 -0
- package/dist/chunk-R4MD5YMV.js.map +1 -0
- package/dist/index.d.ts +62 -32
- package/dist/index.js +151 -64
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +129 -134
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-GRIYYG45.js +0 -1861
- package/dist/chunk-GRIYYG45.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -409,56 +409,86 @@ declare namespace themeRender {
|
|
|
409
409
|
export { themeRender_injectCss as injectCss, themeRender_renderTheme as renderTheme };
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
-
type
|
|
412
|
+
type Tier = 'parsing' | 'match' | 'recruiter';
|
|
413
413
|
type AtsCheckWeight = 'high' | 'medium' | 'low';
|
|
414
414
|
type AtsRating = 'excellent' | 'good' | 'needs-work' | 'poor';
|
|
415
|
-
type
|
|
416
|
-
|
|
415
|
+
type CheckStatus = 'pass' | 'warn' | 'fail' | 'skipped';
|
|
416
|
+
type Grade = 'A' | 'B' | 'C' | 'D' | 'F';
|
|
417
|
+
interface CheckResult {
|
|
417
418
|
id: string;
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
passed: boolean;
|
|
419
|
+
tier: Tier;
|
|
420
|
+
status: CheckStatus;
|
|
421
421
|
score: number;
|
|
422
|
+
weight: AtsCheckWeight;
|
|
422
423
|
message: string;
|
|
423
|
-
|
|
424
|
+
hints: string[];
|
|
424
425
|
}
|
|
425
|
-
interface
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
matchPercentage: number;
|
|
426
|
+
interface TierResult {
|
|
427
|
+
score: number;
|
|
428
|
+
grade: Grade;
|
|
429
|
+
checks: CheckResult[];
|
|
430
430
|
}
|
|
431
|
-
interface
|
|
432
|
-
|
|
433
|
-
|
|
431
|
+
interface KnockoutSignal {
|
|
432
|
+
signal: string;
|
|
433
|
+
evidence: string;
|
|
434
|
+
recommendation: string;
|
|
434
435
|
}
|
|
435
|
-
interface
|
|
436
|
+
interface TieredAtsResult {
|
|
436
437
|
score: number;
|
|
437
438
|
rating: AtsRating;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
439
|
+
tiers: {
|
|
440
|
+
parsing: TierResult;
|
|
441
|
+
match?: TierResult;
|
|
442
|
+
recruiter: TierResult;
|
|
443
|
+
};
|
|
444
|
+
knockouts: KnockoutSignal[];
|
|
441
445
|
summary: string;
|
|
442
446
|
}
|
|
443
447
|
interface AtsOptions {
|
|
444
448
|
language?: string;
|
|
445
449
|
jobDescription?: string;
|
|
446
450
|
threshold?: number;
|
|
451
|
+
config?: AtsConfig;
|
|
452
|
+
}
|
|
453
|
+
interface AtsConfig {
|
|
454
|
+
weights: {
|
|
455
|
+
tiers: {
|
|
456
|
+
parsing: number;
|
|
457
|
+
match: number;
|
|
458
|
+
recruiter: number;
|
|
459
|
+
};
|
|
460
|
+
checks: Record<string, AtsCheckWeight>;
|
|
461
|
+
};
|
|
462
|
+
thresholds: {
|
|
463
|
+
rating: {
|
|
464
|
+
excellent: number;
|
|
465
|
+
good: number;
|
|
466
|
+
needsWork: number;
|
|
467
|
+
};
|
|
468
|
+
grade: {
|
|
469
|
+
A: number;
|
|
470
|
+
B: number;
|
|
471
|
+
C: number;
|
|
472
|
+
D: number;
|
|
473
|
+
};
|
|
474
|
+
seniorYoeCutoff: number;
|
|
475
|
+
wordCount: {
|
|
476
|
+
min: number;
|
|
477
|
+
max: number;
|
|
478
|
+
seniorMax: number;
|
|
479
|
+
};
|
|
480
|
+
bulletsPerRole: {
|
|
481
|
+
min: number;
|
|
482
|
+
max: number;
|
|
483
|
+
seniorMax: number;
|
|
484
|
+
};
|
|
485
|
+
};
|
|
486
|
+
disable: string[];
|
|
487
|
+
locale: string;
|
|
447
488
|
}
|
|
448
489
|
|
|
449
|
-
|
|
450
|
-
* Run ATS analysis on a resume.
|
|
451
|
-
*
|
|
452
|
-
* Performs deterministic, offline checks:
|
|
453
|
-
* 1. Generic best-practice checks (contact, content, structure)
|
|
454
|
-
* 2. Optional job-description keyword matching
|
|
455
|
-
*
|
|
456
|
-
* @param resume - Validated resume data
|
|
457
|
-
* @param options - ATS analysis options
|
|
458
|
-
* @returns Full ATS analysis result with score, checks, and suggestions
|
|
459
|
-
*/
|
|
460
|
-
declare function analyzeAts(resume: ResumeSchema, options?: AtsOptions): AtsResult;
|
|
490
|
+
declare function analyzeAts(resume: ResumeSchema, options?: AtsOptions): TieredAtsResult;
|
|
461
491
|
|
|
462
492
|
declare const program: Command;
|
|
463
493
|
|
|
464
|
-
export { type AtsOptions, type
|
|
494
|
+
export { type AtsOptions, type TieredAtsResult, analyzeAts, loadResumeFiles, loadTheme, processResumeData, program, themeRender };
|
package/dist/index.js
CHANGED
|
@@ -5,10 +5,13 @@ import {
|
|
|
5
5
|
analyzeAts,
|
|
6
6
|
generateResumeYaml,
|
|
7
7
|
getInstalledVersion,
|
|
8
|
+
getRubricEntry,
|
|
8
9
|
isThemeInstalled,
|
|
10
|
+
listRubricMarkdown,
|
|
11
|
+
loadConfig,
|
|
9
12
|
loadTheme,
|
|
10
13
|
processResumeData
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-R4MD5YMV.js";
|
|
12
15
|
|
|
13
16
|
// src/index.ts
|
|
14
17
|
import { Command } from "commander";
|
|
@@ -129,97 +132,85 @@ function handleCommandError(error, command, debug = false) {
|
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
// src/commands/validate.ts
|
|
132
|
-
function formatAtsReport(result, debug,
|
|
133
|
-
const scoreColor = result.score >= 75 ?
|
|
135
|
+
function formatAtsReport(result, debug, chalk7) {
|
|
136
|
+
const scoreColor = result.score >= 75 ? chalk7.green : result.score >= 60 ? chalk7.yellow : chalk7.red;
|
|
134
137
|
console.log("");
|
|
135
|
-
console.log(
|
|
138
|
+
console.log(chalk7.bold("=== ATS Analysis Report ==="));
|
|
136
139
|
console.log("");
|
|
137
|
-
console.log(
|
|
140
|
+
console.log(
|
|
141
|
+
` Score: ${scoreColor(chalk7.bold(`${result.score}/100`))} (${result.rating.replace("-", " ")})`
|
|
142
|
+
);
|
|
138
143
|
console.log(` ${result.summary}`);
|
|
139
144
|
console.log("");
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
categories[check.category] = [check];
|
|
145
|
-
} else {
|
|
146
|
-
list.push(check);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
const categoryLabels = {
|
|
150
|
-
contact: "Contact Information",
|
|
151
|
-
content: "Content Quality",
|
|
152
|
-
structure: "Resume Structure",
|
|
153
|
-
keywords: "Keywords"
|
|
145
|
+
const tierLabels = {
|
|
146
|
+
parsing: "Parsing",
|
|
147
|
+
recruiter: "Recruiter",
|
|
148
|
+
match: "JD Match"
|
|
154
149
|
};
|
|
155
|
-
for (const [
|
|
156
|
-
const label =
|
|
157
|
-
console.log(
|
|
158
|
-
for (const check of checks) {
|
|
159
|
-
if (!debug && check.
|
|
160
|
-
const icon = check.
|
|
161
|
-
const scoreText =
|
|
150
|
+
for (const [tierName, tier] of Object.entries(result.tiers)) {
|
|
151
|
+
const label = tierLabels[tierName] ?? tierName;
|
|
152
|
+
console.log(chalk7.bold(` ${label} (${tier.score}/100, grade ${tier.grade})`));
|
|
153
|
+
for (const check of tier.checks) {
|
|
154
|
+
if (!debug && (check.status === "pass" || check.status === "skipped")) continue;
|
|
155
|
+
const icon = check.status === "pass" ? chalk7.green("v") : check.status === "skipped" ? chalk7.dim("-") : check.status === "warn" ? chalk7.yellow("!") : chalk7.red("x");
|
|
156
|
+
const scoreText = chalk7.dim(`[${check.score}]`);
|
|
162
157
|
console.log(` ${icon} ${check.message} ${scoreText}`);
|
|
163
|
-
|
|
164
|
-
console.log(
|
|
158
|
+
for (const hint of check.hints) {
|
|
159
|
+
console.log(chalk7.dim(` -> ${hint}`));
|
|
165
160
|
}
|
|
166
161
|
}
|
|
167
162
|
console.log("");
|
|
168
163
|
}
|
|
169
|
-
if (result.
|
|
170
|
-
console.log(
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (kw.matched.length > 0) {
|
|
175
|
-
console.log(chalk6.green(` \u2713 Matched: ${kw.matched.join(", ")}`));
|
|
176
|
-
}
|
|
177
|
-
if (kw.missing.length > 0) {
|
|
178
|
-
console.log(chalk6.red(` \u2717 Missing: ${kw.missing.join(", ")}`));
|
|
179
|
-
console.log(chalk6.dim(" \u2192 Consider incorporating these keywords into your resume where relevant."));
|
|
164
|
+
if (result.knockouts.length > 0) {
|
|
165
|
+
console.log(chalk7.bold(" Knockout Signals"));
|
|
166
|
+
for (const k of result.knockouts) {
|
|
167
|
+
console.log(chalk7.red(` ! ${k.signal}: ${k.evidence}`));
|
|
168
|
+
console.log(chalk7.dim(` -> ${k.recommendation}`));
|
|
180
169
|
}
|
|
181
170
|
console.log("");
|
|
182
171
|
}
|
|
183
|
-
console.log(
|
|
172
|
+
console.log(chalk7.dim("==========================="));
|
|
184
173
|
}
|
|
185
174
|
async function validateAction(options) {
|
|
186
|
-
const
|
|
187
|
-
console.log(
|
|
175
|
+
const chalk7 = (await import("chalk")).default;
|
|
176
|
+
console.log(chalk7.blue("Starting resuml validate..."));
|
|
188
177
|
try {
|
|
189
178
|
const inputPath = options.resume;
|
|
190
179
|
const { yamlContents } = await loadResumeFiles(inputPath);
|
|
191
|
-
console.log(
|
|
180
|
+
console.log(chalk7.blue("Validating resume data..."));
|
|
192
181
|
let resumeData;
|
|
193
182
|
try {
|
|
194
183
|
resumeData = await processResumeData(yamlContents);
|
|
195
|
-
console.log(
|
|
184
|
+
console.log(chalk7.green("\u2713 Resume data is valid against the schema!"));
|
|
196
185
|
} catch (error) {
|
|
197
186
|
handleCommandError(error, "validate", options.debug);
|
|
198
187
|
return;
|
|
199
188
|
}
|
|
200
189
|
if (options.ats) {
|
|
201
|
-
console.log(
|
|
190
|
+
console.log(chalk7.blue("Running ATS analysis..."));
|
|
202
191
|
let jobDescription;
|
|
203
192
|
if (options.jd) {
|
|
204
193
|
try {
|
|
205
194
|
jobDescription = fs3.readFileSync(options.jd, "utf8");
|
|
206
195
|
} catch {
|
|
207
|
-
console.error(
|
|
196
|
+
console.error(chalk7.red(`Failed to read job description file: ${options.jd}`));
|
|
208
197
|
return;
|
|
209
198
|
}
|
|
210
199
|
}
|
|
200
|
+
const cfg = loadConfig(options.config ? { configPath: options.config } : {});
|
|
211
201
|
const result = analyzeAts(resumeData, {
|
|
212
|
-
language:
|
|
213
|
-
jobDescription
|
|
202
|
+
language: cfg.locale,
|
|
203
|
+
jobDescription,
|
|
204
|
+
config: cfg
|
|
214
205
|
});
|
|
215
206
|
if (options.format === "json") {
|
|
216
207
|
console.log(JSON.stringify(result, null, 2));
|
|
217
208
|
} else {
|
|
218
|
-
formatAtsReport(result, !!options.debug,
|
|
209
|
+
formatAtsReport(result, !!options.debug, chalk7);
|
|
219
210
|
}
|
|
220
211
|
const threshold = options.atsThreshold ? parseInt(options.atsThreshold, 10) : void 0;
|
|
221
212
|
if (threshold !== void 0 && result.score < threshold) {
|
|
222
|
-
console.error(
|
|
213
|
+
console.error(chalk7.red(`
|
|
223
214
|
ATS score ${result.score} is below threshold ${threshold}.`));
|
|
224
215
|
process.exit(1);
|
|
225
216
|
}
|
|
@@ -232,17 +223,17 @@ ATS score ${result.score} is below threshold ${threshold}.`));
|
|
|
232
223
|
// src/commands/tojson.ts
|
|
233
224
|
import fs4 from "fs";
|
|
234
225
|
async function toJsonAction(options) {
|
|
235
|
-
const
|
|
236
|
-
console.log(
|
|
226
|
+
const chalk7 = (await import("chalk")).default;
|
|
227
|
+
console.log(chalk7.blue("Starting resuml tojson..."));
|
|
237
228
|
try {
|
|
238
229
|
const inputPath = options.resume;
|
|
239
230
|
const { yamlContents } = await loadResumeFiles(inputPath);
|
|
240
|
-
console.log(
|
|
231
|
+
console.log(chalk7.blue("Processing and validating data..."));
|
|
241
232
|
const resumeData = await processResumeData(yamlContents);
|
|
242
|
-
console.log(
|
|
233
|
+
console.log(chalk7.green("Processing and validation successful!"));
|
|
243
234
|
const jsonOutput = JSON.stringify(resumeData, null, 2);
|
|
244
235
|
fs4.writeFileSync(options.output, jsonOutput, "utf8");
|
|
245
|
-
console.log(
|
|
236
|
+
console.log(chalk7.green(`Successfully wrote output to ${options.output}`));
|
|
246
237
|
} catch (error) {
|
|
247
238
|
handleCommandError(error, "tojson", options.debug);
|
|
248
239
|
}
|
|
@@ -454,15 +445,37 @@ async function initAction(options) {
|
|
|
454
445
|
const name = await ask(rl, "Your full name", "John Doe");
|
|
455
446
|
const email = await ask(rl, "Email address", "john@example.com");
|
|
456
447
|
const label = await ask(rl, "Professional title/label", "Software Engineer");
|
|
457
|
-
const
|
|
448
|
+
const yaml2 = generateResumeYaml(name, email, label);
|
|
458
449
|
fs7.mkdirSync(path4.dirname(fullPath), { recursive: true });
|
|
459
|
-
fs7.writeFileSync(fullPath,
|
|
450
|
+
fs7.writeFileSync(fullPath, yaml2, "utf8");
|
|
451
|
+
const configPath = path4.join(path4.dirname(fullPath), "resuml.config.yaml");
|
|
452
|
+
const template = `# resuml ATS configuration
|
|
453
|
+
# Override defaults; everything is optional.
|
|
454
|
+
# Run \`resuml ats config --print\` to see the merged effective config.
|
|
455
|
+
ats:
|
|
456
|
+
weights:
|
|
457
|
+
tiers:
|
|
458
|
+
parsing: 30
|
|
459
|
+
match: 50
|
|
460
|
+
recruiter: 20
|
|
461
|
+
thresholds:
|
|
462
|
+
seniorYoeCutoff: 10
|
|
463
|
+
disable: []
|
|
464
|
+
`;
|
|
465
|
+
try {
|
|
466
|
+
fs7.writeFileSync(configPath, template, { encoding: "utf8", flag: "wx" });
|
|
467
|
+
console.log(chalk3.green(`Created resuml.config.yaml`));
|
|
468
|
+
} catch (err) {
|
|
469
|
+
if (err.code !== "EEXIST") throw err;
|
|
470
|
+
}
|
|
460
471
|
console.log(chalk3.green(`
|
|
461
472
|
\u2705 Created ${outputPath}`));
|
|
462
473
|
console.log(chalk3.blue("\nNext steps:"));
|
|
463
474
|
console.log(` 1. Edit ${outputPath} to fill in your details`);
|
|
464
475
|
console.log(" 2. Run " + chalk3.cyan("resuml validate --resume " + outputPath));
|
|
465
|
-
console.log(
|
|
476
|
+
console.log(
|
|
477
|
+
" 3. Run " + chalk3.cyan("resuml render --resume " + outputPath + " --theme stackoverflow")
|
|
478
|
+
);
|
|
466
479
|
} finally {
|
|
467
480
|
rl.close();
|
|
468
481
|
}
|
|
@@ -525,6 +538,30 @@ async function pdfAction(options) {
|
|
|
525
538
|
try {
|
|
526
539
|
const page = await browser.newPage();
|
|
527
540
|
await page.setContent(htmlOutput, { waitUntil: "networkidle" });
|
|
541
|
+
const bodyText = await page.evaluate(() => document.body.innerText || "");
|
|
542
|
+
const bodyWords = bodyText.trim().split(/\s+/).filter(Boolean).length;
|
|
543
|
+
const resumeContentParts = [];
|
|
544
|
+
if (resumeData.basics?.summary) resumeContentParts.push(resumeData.basics.summary);
|
|
545
|
+
for (const w of resumeData.work || []) {
|
|
546
|
+
if (w.summary) resumeContentParts.push(w.summary);
|
|
547
|
+
resumeContentParts.push(...w.highlights || []);
|
|
548
|
+
}
|
|
549
|
+
for (const p of resumeData.projects || []) {
|
|
550
|
+
if (p.description) resumeContentParts.push(p.description);
|
|
551
|
+
resumeContentParts.push(...p.highlights || []);
|
|
552
|
+
}
|
|
553
|
+
for (const s of resumeData.skills || []) {
|
|
554
|
+
if (s.name) resumeContentParts.push(s.name);
|
|
555
|
+
resumeContentParts.push(...s.keywords || []);
|
|
556
|
+
}
|
|
557
|
+
const resumeWords = resumeContentParts.join(" ").split(/\s+/).filter(Boolean).length;
|
|
558
|
+
if (resumeWords > 0 && bodyWords < resumeWords * 0.7) {
|
|
559
|
+
console.warn(
|
|
560
|
+
chalk4.yellow(
|
|
561
|
+
`pdf-text-extractable: rendered text ${bodyWords} words vs resume ${resumeWords} (under 70%). Theme may use image-based glyphs.`
|
|
562
|
+
)
|
|
563
|
+
);
|
|
564
|
+
}
|
|
528
565
|
const pdfBuffer = await page.pdf({
|
|
529
566
|
format,
|
|
530
567
|
margin,
|
|
@@ -533,6 +570,14 @@ async function pdfAction(options) {
|
|
|
533
570
|
});
|
|
534
571
|
fs8.mkdirSync(path5.dirname(path5.resolve(outputPath)), { recursive: true });
|
|
535
572
|
fs8.writeFileSync(outputPath, pdfBuffer);
|
|
573
|
+
const sizeMb = pdfBuffer.length / (1024 * 1024);
|
|
574
|
+
if (sizeMb > 2.5) {
|
|
575
|
+
console.warn(
|
|
576
|
+
chalk4.yellow(
|
|
577
|
+
`pdf-size-under-2.5mb: PDF is ${sizeMb.toFixed(2)} MB (Greenhouse limit 2.5 MB).`
|
|
578
|
+
)
|
|
579
|
+
);
|
|
580
|
+
}
|
|
536
581
|
console.log(chalk4.green(`\u2705 Successfully generated ${outputPath}`));
|
|
537
582
|
} finally {
|
|
538
583
|
await browser.close();
|
|
@@ -552,7 +597,9 @@ function listThemes() {
|
|
|
552
597
|
console.log(
|
|
553
598
|
` ${"Status".padEnd(10)}${"Name".padEnd(nameWidth)}${"Package".padEnd(pkgWidth)}Description`
|
|
554
599
|
);
|
|
555
|
-
console.log(
|
|
600
|
+
console.log(
|
|
601
|
+
` ${"\u2500".repeat(10)}${"\u2500".repeat(nameWidth)}${"\u2500".repeat(pkgWidth)}${"\u2500".repeat(30)}`
|
|
602
|
+
);
|
|
556
603
|
for (const theme of KNOWN_THEMES) {
|
|
557
604
|
const installed = isThemeInstalled(theme.pkg);
|
|
558
605
|
const version = installed ? getInstalledVersion(theme.pkg) : null;
|
|
@@ -579,14 +626,18 @@ function installTheme(name) {
|
|
|
579
626
|
execSync(`npm install ${pkg}`, { stdio: "inherit" });
|
|
580
627
|
console.log(chalk5.green(`
|
|
581
628
|
\u2705 Successfully installed ${pkg}`));
|
|
582
|
-
console.log(
|
|
629
|
+
console.log(
|
|
630
|
+
chalk5.blue(`
|
|
583
631
|
Use it with: ${chalk5.cyan(`resuml render --theme ${known?.name || name}`)}
|
|
584
|
-
`)
|
|
632
|
+
`)
|
|
633
|
+
);
|
|
585
634
|
} catch {
|
|
586
635
|
console.error(chalk5.red(`
|
|
587
636
|
\u274C Failed to install ${pkg}`));
|
|
588
|
-
console.error(
|
|
589
|
-
`
|
|
637
|
+
console.error(
|
|
638
|
+
chalk5.yellow(`Make sure the package exists: https://www.npmjs.com/package/${pkg}
|
|
639
|
+
`)
|
|
640
|
+
);
|
|
590
641
|
}
|
|
591
642
|
}
|
|
592
643
|
function themesAction(options) {
|
|
@@ -603,6 +654,32 @@ async function mcpAction() {
|
|
|
603
654
|
await startMcpServer();
|
|
604
655
|
}
|
|
605
656
|
|
|
657
|
+
// src/commands/ats.ts
|
|
658
|
+
import yaml from "yaml";
|
|
659
|
+
import chalk6 from "chalk";
|
|
660
|
+
function atsExplain(id) {
|
|
661
|
+
if (!id) {
|
|
662
|
+
console.log(listRubricMarkdown());
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const entry = getRubricEntry(id);
|
|
666
|
+
if (!entry) {
|
|
667
|
+
console.error(chalk6.red(`Unknown rubric id: ${id}`));
|
|
668
|
+
process.exit(1);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
console.log(chalk6.bold(entry.id));
|
|
672
|
+
console.log(` Tier: ${entry.tier}`);
|
|
673
|
+
console.log(` Weight: ${entry.weight}`);
|
|
674
|
+
console.log(` Evidence: ${entry.evidenceLevel}`);
|
|
675
|
+
console.log(` Description: ${entry.description}`);
|
|
676
|
+
if (entry.source) console.log(` Source: ${entry.source}`);
|
|
677
|
+
}
|
|
678
|
+
function atsConfigPrint(opts) {
|
|
679
|
+
const cfg = loadConfig(opts.config ? { configPath: opts.config } : {});
|
|
680
|
+
console.log(yaml.stringify({ ats: cfg }));
|
|
681
|
+
}
|
|
682
|
+
|
|
606
683
|
// src/utils/themeRender.ts
|
|
607
684
|
var themeRender_exports = {};
|
|
608
685
|
__export(themeRender_exports, {
|
|
@@ -671,7 +748,10 @@ function getCliVersion() {
|
|
|
671
748
|
}
|
|
672
749
|
var program = new Command();
|
|
673
750
|
program.name("resuml").description("CLI tool for managing resuml resume files.").version(getCliVersion());
|
|
674
|
-
program.command("validate").description("Validates resume data against the schema.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("--debug", "Show detailed validation errors.").option("--ats", "Run ATS (Applicant Tracking System) compatibility analysis.").option("--jd <path>", "Path to a job description file for keyword matching (requires --ats).").option(
|
|
751
|
+
program.command("validate").description("Validates resume data against the schema.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("--debug", "Show detailed validation errors.").option("--ats", "Run ATS (Applicant Tracking System) compatibility analysis.").option("--jd <path>", "Path to a job description file for keyword matching (requires --ats).").option(
|
|
752
|
+
"--ats-threshold <score>",
|
|
753
|
+
"Minimum ATS score (0-100). Exit with code 1 if below threshold."
|
|
754
|
+
).option("--format <type>", "Output format for ATS results (text or json).", "text").option("--config <path>", "Path to resuml.config.yaml (default: ./resuml.config.yaml).").action(validateAction);
|
|
675
755
|
program.command("tojson").description("Converts YAML resume data to JSON format.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-o, --output <file>", "Output JSON file path.", "resume.json").option("--debug", "Show detailed validation and processing information.").action(toJsonAction);
|
|
676
756
|
program.command("render").description("Renders the resume data using a specified theme.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-t, --theme <name>", "Theme name (e.g., stackoverflow, react).").option("-o, --output <file>", "Output file path.").option("--format <type>", "Output format (html or pdf).", "html").option("--language <code>", "Language code for localization.", "en").option("--debug", "Show detailed validation and processing information.").action(renderAction);
|
|
677
757
|
program.command("dev").description("Start development server with hot-reload.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-t, --theme <name>", "Theme name (e.g., stackoverflow, react).").option("--port <number>", "Port for development server.", "3000").option("--language <code>", "Language code for localization.", "en").option("--debug", "Show detailed validation and processing information.").action(devAction);
|
|
@@ -679,6 +759,13 @@ program.command("init").description("Scaffold a starter resume.yaml file with al
|
|
|
679
759
|
program.command("pdf").description("Export resume as PDF using Playwright.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-t, --theme <name>", "Theme name (e.g., stackoverflow, react).").option("-o, --output <file>", "Output PDF file path.", "resume.pdf").option("--language <code>", "Language code for localization.", "en").option("--format <size>", "Page format: A4 or Letter.", "A4").option("--margin <values>", 'Page margins (e.g., "10mm" or "10mm,15mm,10mm,15mm").').option("--debug", "Show detailed validation and processing information.").action(pdfAction);
|
|
680
760
|
program.command("themes").description("List available JSON Resume themes and install them.").option("--install <name>", "Install a theme by name (e.g., stackoverflow, elegant).").action(themesAction);
|
|
681
761
|
program.command("mcp").description("Start MCP server for AI agent integration (stdio transport).").action(mcpAction);
|
|
762
|
+
var ats = program.command("ats").description("ATS rubric utilities.");
|
|
763
|
+
ats.command("explain [id]").description("Print rubric entry for a check id, or full rubric if id omitted.").action((id) => {
|
|
764
|
+
atsExplain(id);
|
|
765
|
+
});
|
|
766
|
+
ats.command("config").description("Print the effective merged ATS config.").option("--print", "Print the merged config (default action).").option("--config <path>", "Path to resuml.config.yaml.").action((opts) => {
|
|
767
|
+
atsConfigPrint(opts);
|
|
768
|
+
});
|
|
682
769
|
if (process.env["NODE_ENV"] !== "test") {
|
|
683
770
|
void (async () => {
|
|
684
771
|
try {
|