A CLI-Powered Resume You Can Ship as an npm Package

Fresh off Ali Churcher’s CSS Grid resume concept, here’s a different angle: turn that idea into a reusable, interactive resume that lives entirely in the terminal. With Node.js and a couple of well-known libraries, you can scaffold a project that runs with a single npx command—no manual HTML edits each time you update your experience.

Project Setup

Start by creating a project folder and initializing it with npm or Yarn. The project name you choose becomes the package name when you publish to the npm registry.

mkdir your-project && cd "$_"

## npm
npm init

## Yarn
yarn init

Next, create two files: index.js will hold the application logic, and data.json will store your resume data. You can open both from the command line:

touch index.js && touch data.json

Data Model and Interactive Prompts

The core idea is to drive the resume data through an interactive command-line interface. You need two things: a data object and a prompt system—and Inquirer.js handles most of the heavy lifting for the latter.

Start with data.json. The example below defines the object keys and sub-sections you’ll use for each step of the interface. Feel free to adapt it to your own resume’s structure.

{
  "Education": [
    "Some info",
    "Less important info",
    "Etc, etc."
  ],
  "Experience": [
    "Some info",
    "Less important info",
    "Etc, etc."
  ],
  "Contact": [
    "A way to contact you"
  ]
}

To make the terminal output more readable, add chalk for color and text styling. Install both libraries:

yarn add inquirer chalk

Now open index.js and add the main application code:

#!/usr/bin/env node

"use strict";

const inquirer = require("inquirer");
const chalk = require("chalk");
const data = require("./data.json");

// add response color
const response = chalk.bold.blue;

const resumeOptions = {
  type: "list",
  name: "resumeOptions",
  message: "What do you want to know",
  choices: [...Object.keys(data), "Exit"]
};

function showResume() {
  console.log("Hello, this is my resume");
  handleResume();
}

function handleResume() {
  inquirer.prompt(resumeOptions).then(answer => {
    if (answer.resumeOptions == "Exit") return;

    const options = data[`${answer.resumeOptions}`]
    if (options) {
      console.log(response(new inquirer.Separator()));
      options.forEach(info => {
        console.log(response("|   => " + info));
      });
      console.log(response(new inquirer.Separator()));
    }

    inquirer
      .prompt({
        type: "list",
        name: "exitBack",
        message: "Go back or Exit?",
        choices: ["Back", "Exit"]
      }).then(choice => {
        if (choice.exitBack == "Back") {
          handleResume();
        } else {
          return;
        }
      });
  }).catch(err => console.log('Ooops,', err))
}

showResume();

Breaking Down the Logic

The top of the file imports the required modules and sets up chalk’s color styles for a more polished output.

const inquirer = require("inquirer");
const chalk = require("chalk");
const data = require("./data.json");

// add response color
const response = chalk.bold.blue;

The resumeOptions array defines the main menu shown when the app runs. The choices field maps to the keys in your data object and adds an “Exit” option so you can stop the program cleanly.

const resumeOptions = {
  type: "list",
  name: "resumeOptions",
  message: "What do you want to know",
  choices: [...Object.keys(data), "Exit"]
};

The showResume() function serves as the entry point for the user—it prints a welcome message and kicks off the handleResume() flow.

function showResume() {
  console.log("Hello, this is my resume");
  handleResume();
}

handleResume() checks whether the user chose to exit; if not, it reads answer.resumeOptions and lists the matching sections from data.json—say, Education, Experience, and Contact. Inquirer’s new inquirer.Separator() adds visual breaks between those blocks.

To let users move back through the sections, a second inquirer.prompt offers only “Back” and “Exit.” Choosing “Back” recalls handleResume(), while “Exit” quits. A catch block rounds off the function for error handling.

Publishing to npm

You can test the app locally with:

node index.js

That works, but a real package should run from anywhere. Publishing makes that possible:

  1. Create an account at npmjs.com if you don’t already have one.
  2. Authenticate locally with npm adduser and provide your credentials.
  3. In package.json, add the bin entry so the CLI command maps to index.js:
"bin": {
  "your-package-name": "./index.js"
}
  • Add a README.md so the package page looks professional.
  • Publish with npm publish.
  • npm publish --access=public

    To push updates after editing the resume data or logic, follow semantic versioning to bump the version:

    npm version patch // 1.0.1
    npm version minor // 1.1.0
    npm version major // 2.0.0

    Then republish:

    npm publish

    Running on Any Machine

    Once the package is live, anyone—or you, from a fresh machine—can run it immediately with:

    npx your-package-name

    npx runs the package without a global install, and it ships with npm, so there’s no extra tooling setup needed.

    The final project is minimal, but the same pattern—data in JSON, prompts via Inquirer, and a small entry point—scales naturally into more complex CLIs. The source code for this project is on GitHub for reference.