npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

pqbl

v0.2.6

Published

A CLI tool for generating pQBL courses using AI.

Readme

pqbl — A CLI for AI-generated educational quizzes

The pqbl CLI automates the process of generating customized educational quizzes. By using generative AI it generates multiple-choice questions that are based on your own learning material, with informative feedback to each answer option.

The generation process follows a pipeline workflow:

  1. Create a new project
  2. Give a description of the course
  3. Provide resources (learning material files in pdf, docx, pptx or text format)
  4. From the resources, extract the skillmap (a list of the learning objectives)
  5. Review the skillmap
  6. From the skillmap, generate the course (a list of modules, containing pages of questions)
  7. Review the course
  8. Export the course and run on the Torus learning platform
  9. Gather statistics and improve

Table of Contents

Prerequisites

  • An installation of NodeJS
  • An active OpenAI account, with a valid API key

Install

Assuming all prerequisites have been met, installing is as simple as:

npm install --global pqbl

Check that installation worked:

pqbl --version # Should print something like: 0.2.0

For generation to work, you need to set the PQBL_OPENAI_API_KEY environment variable with your OpenAI API key. You can copy and paste the following code snippet into your terminal (replacing your-api-key with your actual key):

# bash
echo 'export PQBL_OPENAI_API_KEY="your-api-key"' >> "$HOME/.bashrc" && source "$HOME/.bashrc"

# zsh
echo 'export PQBL_OPENAI_API_KEY="your-api-key"' >> "$HOME/.zshrc" && source "$HOME/.zshrc"

Done! Check out the quickstart to get up and running.

Update

npm update --global pqbl

Uninstall

npm uninstall --global pqbl

Quickstart

[!TIP] Get more info about a command with the -h flag. For general help, use:

pqbl -h

This is a practical walkthrough that will take you from nothing to a finished course in about 10 minutes. When you are done you should know how to:

  • Create a new project
  • Configure a project
  • Generate a skillmap
  • Generate a course
  • Export a course

Creating a project

Let's start by creating a new pqbl project. A project is a workspace folder containing all your source material and generated content. Most pqbl commands can only run in a project folder.

We create a new project like this:

pqbl project new "History 101"

You should see the following output:

✓ Created new project: /Users/orn/kth/test-projects/history_101

Change into the new project:

cd history_101

The directory only contains one file:

.
└── pqbl.json

This pqbl.json config file is how pqbl knows you are inside a project. As long as you are in this folder or any subfolders, everything will work as expected. Note that nested projects are not supported.

Next up we will see how to edit the config file.

Configuring your project

The pqbl.json file is the main configuration file for the pqbl tool. It uses a custom JSON file format and can be edited in any text editor.

The default config file from the previous section looks like this:

{
  "title": "History 101",
  "description": "",
  "language": "en",
  "learner": "",
  "include": "",
  "exclude": "",
  "modules": []
}

All fields are strings, except modules which is a list of strings.

The title has been added automatically, but we need to edit the rest of the fields. To be able to generate, we have to add a description and at least one module to the modules list.

With language, we can specify a custom target language, using ISO 369 2-letter language codes. This can be helpful if the course material is in another language than the course is taught in. For example, if we want to generate a course that is taught in Spanish, we set the language to es. You can override the language specified in the config by using the --language flag when generating. The default config is set to English (en), but you can change or remove it if you want to. If no language is specified, it is inferred from the given learning material. For this tutorial, we will not use the language setting, so we simply delete it.

We can use the learner field to describe our target audience. This will make the generated content more appropriate for a specific academic level, or any special needs. This field is not required, but recommended for more relevant results.

The include and exclude fields can be used to explicitly specify topics that should be included or avoided. We do not need them here, so we can omit them.

Our updated config file looks like this:

{
  "title": "History 101",
  "description": "A course about world history.",
  "learner": "High school students with ADHD",
  "modules": [
    "Early civilization",
    "Modern times",
    "The future"
  ]
}

We can use the pqbl config command to get a quick view of the config file from the command line:

pqbl config

This will print the config to the terminal:

{
  "title": "History 101",
  "description": "A course about world history.",
  "learner": "High school students with ADHD",
  "modules": [
    "Early civilization",
    "Modern times",
    "The future"
  ]
}

Adding resources

In order to generate we have to add some resources as well. These are files with course material that is used to provide context to the AI when generating. Supported file formats are: .pdf, .docx, .pptx and plain-text files (must be .txt).

We create a resources folder and add some resources. This name is special to the pqbl tool, and is where it will look for resource files by default. To use another directory, add the --resources option.

Our project now looks like this:

.
├── pqbl.json
└── resources
    ├── early-civilization-lecture.pdf
    ├── modern-times-lecture.pdf
    ├── modern-times-test.pdf
    └── the-future-discussion.pdf

Now we are ready to generate!

Generating a skillmap

[!TIP] Use the --language flag to specify a target language. See pqbl skillmap generate -h for more info and options.

To generate the skillmap:

pqbl skillmap generate

This will ask you to verify your generation settings, such as which resources folder you are using, what language, and so on. It will look something like this:

Confirm settings before generating.
--------------------------------------------------------------------------------
Settings
  Language: unspecified (will be inferred from resources)
  Resources: /path/to/history_101/resources
  Output: /path/to/history_101/skillmaps
Modules
   1 Early civilization
   2 Modern times
   3 The future
--------------------------------------------------------------------------------
> Continue? (y/n)

Press y and enter if you are happy with the settings or n to abort

After generation is done, you will see output similar to this:

✓ Generated skillmap: /path/to/history_101/skillmaps/world_history_101_20250930t094355428z.json

Our project now looks like this:

.
├── pqbl.json
├── resources
│   └── ... # omitted
└── skillmaps
    └── world_history_101_20250930t094355428z.json

By default new skillmaps are added to a skillmaps folder (created automatically if it does not exist) in the root of the project. This can be overridden for each generation run by using the --output option.

You can now review the skillmap. It is a JSON file containing a list of modules. Each module has a title and an objective, which is broken down into a list of skills. These directly affect the next generation step:

  • Each skillmap module corresponds to a course module (1-1 mapping)
  • Each skillmap objective corresponds to a course page (1-1 mapping)
  • Each skillmap skill corresponds to multiple course questions (1-many mapping)

See the example skillmap for more details.

After reviewing, it is time to generate the course.

Generating a course

[!TIP] Use the --language flag to specify a target language. See pqbl course generate -h for more info and options.

The course generation is a bit different, since it depends on the skillmap. Add the --skillmap flag to use a specific skillmap. If no skillmap is specified, the most recently edited skillmap in the skillmaps folder is used. When reviewing multiple skillmaps at the same time, it is safest to specify the exact skillmap you want to use.

Assuming we want to use the most recent skillmap, we can simply run:

pqbl course generate

And verify with y or abort with n:

Confirm settings before generating.
--------------------------------------------------------------------------------
Settings
  Language: unspecified (will be inferred from resources)
  Resources: /path/to/history_101/resources
  Skillmap: Latest
  Output: /path/to/history_101/courses
Modules
   1 Early civilization
   2 Modern times
   3 The future
--------------------------------------------------------------------------------
> Continue? (y/n)

After generation is done, you will see output similar to this:

✓ Generated course: /path/to/history_101/courses/history_10_course_20250930t100514155z

This gives us the following project structure:

.
├── courses
│   └── history_10_course_20250930t100514155z
│       ├── info.json
│       ├── module_01_early_civilization
│       │   └── page_01_foundations_of_early_civilizations.json
│       ├── module_02_modern_times
│       │   └── page_01_industrial_revolution_and_colonialism_impact.json
│       └── module_03_the_future
│           └── page_01_future_societies_and_climate_challenges.json
├── pqbl.json
├── resources
│   └── ... # omitted
└── skillmaps
    └── world_history_101_20250930t094355428z.json

Similarly to the skillmap, new courses are added to the courses folder (created automatically if it does not exist) in the root of the project. This can be overridden for each generation run by using the --output option.

The generated course has one directory for each module. Each module directory contains one or more JSON files describing a course page. The page is a collection of questions for a single learning objective.

See the example page for more details.

We have now generated a full course, with the resulting high level view of the project:

.
├── courses/   # All courses
├── pqbl.json  # Configuration
├── resources/ # All resources
└── skillmaps/ # All skillmaps

When you have reviewed the pages and questions, you are ready to export.

Image generation

See pqbl images -h.

Exporting a course

[!WARNING] The generate course command creates modules in the order defined by the modules property in pqbl.json. When loading the stored modules, the pqbl export command assumes that they are in same the same lexicographical order as they were created.

If the order changes, the export may behave unexpectedly.

An optional onboarding module may be added manually after generating the main course modules, even if it was not generated automatically (as long as it uses the same JSON-format as all other modules). However, it must come before all other modules.

Courses are exported with the pqbl course export <course> command, where <course> is a required argument which must be a path to the course you want to export. The path can be relative or absolute.

If we are in the root of the project, it is simplest to use a relative path:

pqbl course export courses/history_10_course_20250930t100514155z

This will print something like:

✓ Exported course: /path/to/history_101/exports/dd1338_alg_export_torus_20250930t105451234z.zip

As before, the exports directory is created if it does not exist. The Torus export format is called the course digest or simply digest. It is a zipped collection of JSON files that models the course, which Torus can import and run.

The next step is to import the exported course into Torus.

Importing a course to Torus

[!NOTE] Only admins can import a course to Torus.

  1. Go to the admin panel image

  2. Click on V2 Ingest Project image

  3. Choose file image

  4. Start ingestion process image

Complete the instructions in Torus to import your course, and now you are done!

Examples

Example skillmap

Here is a small example of a skillmap:

{
  "title": "History 101",
  "description": "A course about world history.",
  "modules": [
    {
      "title": "Early civilization",
      "objectives": [
        {
          "goal": "Understand the foundations of early civilizations.",
          "skills": [
            "Explain the role of agriculture in permanent settlements.",
            "Identify features of Mesopotamian and Egyptian societies."
          ]
        }
      ]
    },
    {
      "title": "Modern times",
      "objectives": [
        {
          "goal": "Examine key events of the modern era.",
          "skills": [
            "Summarize the causes and effects of the Industrial Revolution.",
            "Discuss the global impact of colonialism."
          ]
        }
      ]
    },
    {
      "title": "The future",
      "objectives": [
        {
          "goal": "Explore possible scenarios for future societies.",
          "skills": [
            "Discuss challenges of climate change.",
            "Evaluate the role of emerging technologies."
          ]
        }
      ]
    }
  ]
}

Example page

Below is an example page for the Early civilization module. The question array contains the multiple choice questions that make up the course.

{
  "title": "Early civilization",
  "objective": {
    "goal": "Understand the foundations of early civilizations.",
    "skills": [
      "Explain the role of agriculture in permanent settlements.",
      "Identify features of Mesopotamian and Egyptian societies."
    ]
  },
  "questions": [
    {
      "kind": "mcq",
      "stem": "Why was the development of agriculture a key factor in the rise of early civilizations?",
      "skills": ["Explain the role of agriculture in permanent settlements."],
      "choices": [
        {
          "answer": "It allowed people to produce surplus food, supporting larger populations and permanent settlements.",
          "feedback": "Correct! Surplus food meant people could settle in one place and support growing populations, which led to cities and complex societies.",
          "score": 1
        },
        {
          "answer": "It eliminated the need for trade between groups.",
          "feedback": "Incorrect. Agriculture increased trade, not eliminated it, since groups exchanged surplus goods.",
          "score": 0
        },
        {
          "answer": "It prevented the spread of diseases in early societies.",
          "feedback": "Incorrect. Permanent settlements actually made the spread of diseases more likely.",
          "score": 0
        },
        {
          "answer": "It immediately created modern democratic systems of governance.",
          "feedback": "Incorrect. Governance developed gradually and varied greatly between civilizations.",
          "score": 0
        }
      ]
    },
    {
      "kind": "mcq",
      "stem": "Which of the following is a defining feature of the Mesopotamian and Egyptian civilizations?",
      "skills": ["Identify features of Mesopotamian and Egyptian societies."],
      "choices": [
        {
          "answer": "Both developed along major rivers that supported agriculture.",
          "feedback": "Correct! Mesopotamia was centered around the Tigris and Euphrates, and Egypt around the Nile, which were crucial for farming.",
          "score": 1
        },
        {
          "answer": "Both rejected religious practices in favor of purely secular societies.",
          "feedback": "Incorrect. Religion was central in both civilizations, shaping culture and governance.",
          "score": 0
        },
        {
          "answer": "Both avoided writing systems until much later in history.",
          "feedback": "Incorrect. Mesopotamia developed cuneiform and Egypt developed hieroglyphics very early on.",
          "score": 0
        },
        {
          "answer": "Both relied entirely on nomadic lifestyles.",
          "feedback": "Incorrect. The opposite is true — they were among the first permanent civilizations.",
          "score": 0
        }
      ]
    }
  ]
}