From 1f5adfd38a815591fd00a32c66f551ef0e1cbc98 Mon Sep 17 00:00:00 2001 From: riddhij Date: Thu, 21 May 2026 01:58:53 +0530 Subject: [PATCH] feat(cli): add color output for scores, dimensions, and issues --- packages/cli/package.json | 3 +- packages/cli/src/cli.ts | 72 ++++++++++++++++++++++++++------------- pnpm-lock.yaml | 9 +++++ 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 268d091..7dc07c8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -13,7 +13,8 @@ "test": "vitest run --passWithNoTests" }, "dependencies": { - "@cv-builder/core": "workspace:*" + "@cv-builder/core": "workspace:*", + "chalk": "^5.6.2" }, "devDependencies": { "@types/node": "^25.6.2", diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 96328f1..d3b1f06 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -3,6 +3,7 @@ import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { evaluate, listArchetypes } from "@cv-builder/core"; +import chalk from "chalk"; const [, , command, ...args] = process.argv; @@ -22,12 +23,31 @@ async function main() { printHelp(); break; default: - console.error(`Unknown command: ${command}\n`); + console.error(chalk.red(`Unknown command: ${command}\n`)); printHelp(); process.exit(1); } } +function colorScore(score: number): string { + if (score <3) return chalk.red(`${score}/5`); + if (score <4) return chalk.yellow(`${score}/5`); + return chalk.green(`${score}/5`); +} + +function colorDimScore(score: number, max: number): string { + if (score<3) return chalk.red(`${score}/${max}`); + if (score<4) return chalk.yellow(`${score}/${max}`); + return chalk.green(`${score}/${max}`); +} + +function colorBar(score: number, max: number): string { + const filled = "█".repeat(score); + const empty = "░".repeat(max - score); + if (score <3) return chalk.red(filled) + chalk.dim(empty); + if (score <4) return chalk.yellow(filled) + chalk.dim(empty); + return chalk.green(filled) + chalk.dim(empty); +} async function handleEvaluate(args: string[]) { const cvPath = args[0]; const jdFlagIndex = args.indexOf("--jd"); @@ -48,57 +68,61 @@ async function handleEvaluate(args: string[]) { jd: jdContent ? { content: jdContent } : undefined, }); - console.log(`\n CV Score: ${result.score}/5\n`); - console.log(` Archetype detected: ${result.archetype.name}\n`); - console.log(" Dimensions:"); + console.log(`\n CV Score: ${colorScore(result.score)}\n`); + console.log(` Archetype detected: ${chalk.cyan(result.archetype.name)}\n`); + console.log(chalk.bold(" Dimensions:")); for (const dim of result.dimensions) { - const bar = "█".repeat(dim.score) + "░".repeat(dim.maxScore - dim.score); - console.log(` ${bar} ${dim.score}/${dim.maxScore} ${dim.name}`); + const bar = colorBar(dim.score, dim.maxScore); + const score = colorDimScore(dim.score, dim.maxScore); + console.log(` ${bar} ${score} ${dim.name}`); } if (result.strengths.length > 0) { - console.log("\n Strengths:"); + console.log(chalk.bold("\n Strengths:")); for (const s of result.strengths) { - console.log(` ✓ ${s}`); + console.log(` ${chalk.green("✓")} ${s}`); } } if (result.issues.length > 0) { - console.log("\n Issues:"); + console.log(chalk.bold("\n Issues:")); for (const issue of result.issues) { - const icon = issue.severity === "critical" ? "✗" : "⚠"; - console.log(` ${icon} [${issue.severity}] ${issue.element}`); - console.log(` Why: ${issue.why}`); - console.log(` Fix: ${issue.fix}`); + if (issue.severity === "critical") { + console.log(` ${chalk.red("✗")} ${chalk.red(`[${issue.severity}]`)} ${issue.element}`); + } else { + console.log(` ${chalk.yellow("⚠")} ${chalk.yellow(`[${issue.severity}]`)} ${issue.element}`); + } + console.log(` ${chalk.dim("Why:")} ${issue.why}`); + console.log(` ${chalk.dim("Fix:")} ${issue.fix}`); } } - console.log(`\n ATS Compatible: ${result.atsCompatible ? "✓ Yes" : "✗ No"}\n`); + console.log(`\n ATS Compatible: ${result.atsCompatible ? chalk.green("✓ Yes") : chalk.red("✗ No")}\n`); } function handleListArchetypes() { const archetypes = listArchetypes(); - console.log("\n Available role archetypes:\n"); + console.log(chalk.bold("\n Available role archetypes:\n")); for (const a of archetypes) { - console.log(` • ${a.name} (${a.id})`); + console.log(` ${chalk.cyan("•")} ${chalk.bold(a.name)} ${chalk.dim(`(${a.id})`)}`); console.log(` ${a.description}`); - console.log(` Keywords: ${a.keywords.slice(0, 5).join(", ")}...\n`); + console.log(` ${chalk.dim("Keywords:")} ${a.keywords.slice(0, 5).join(", ")}...\n`); } } function printHelp() { console.log(` - CV Builder CLI — Evaluate and tailor your CV + ${chalk.bold("CV Builder CLI")} ${chalk.dim("— Evaluate and tailor your CV")} - USAGE + ${chalk.bold("USAGE")} cv-builder [options] - COMMANDS - evaluate [--jd ] Score your CV (optionally against a JD) - archetypes List available role archetypes - help Show this help + ${chalk.bold("COMMANDS")} + ${chalk.cyan("evaluate")} [--jd ] Score your CV (optionally against a JD) + ${chalk.cyan("archetypes")} List available role archetypes + ${chalk.cyan("help")} Show this help - EXAMPLES + ${chalk.bold("EXAMPLES")} cv-builder evaluate ./my-cv.md cv-builder evaluate ./my-cv.md --jd ./job-description.md cv-builder archetypes diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc6be0c..60b3ef3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@cv-builder/core': specifier: workspace:* version: link:../core + chalk: + specifier: ^5.6.2 + version: 5.6.2 devDependencies: '@types/node': specifier: ^25.6.2 @@ -424,6 +427,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + check-error@2.1.3: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} @@ -887,6 +894,8 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chalk@5.6.2: {} + check-error@2.1.3: {} debug@4.4.3: