PrairieLearn Navigator
A prototype VS Code extension that provides definitions and inlay hints for PrairieLearn assessments and questions.
This extension is still experimental and may change or break.
Features
- Diagnostics for course files: PrairieLearn schema validation for a chosen
PrairieLearn version; missing, incomplete, duplicate, and unused questions;
question directories PrairieLearn can't load for lack of an
info.json;
unused tags and assessment sets; and every file a course names that doesn't
exist (see the
rules).
- For every one of those file references:
- jump to the file: go-to-definition and Ctrl/Cmd-click;
- find its uses: Find All References on a reference or inside the
referenced file, a "N uses" CodeLens at the top of referenced files, and
PrairieLearn: Find Uses of File in the explorer's context menu (works
for images too).
- When a question directory, or an ancestor containing questions, is moved
within
questions/ in VS Code, offer to update all affected assessment IDs,
alternative IDs, and question templates as part of the same operation.
- Preview or apply the same semantic QID reference updates from the CLI with
prairielearn-navigator rename-qids.
- Completions, code lenses, and quick fixes that create missing files, ignore
one diagnostic occurrence in a file, or open that rule's severity setting.
- UUID completions in PrairieLearn info files, generated with
uuidgen by
default, plus quick fixes for a missing or invalid root uuid field.
- Editor-title buttons for switching between a question's
question.html,
info.json, and server.py. The current or missing source file remains
visible but is disabled.
Settings
Set these in .vscode/settings.json. They take precedence over the course's
.pl-navigator.jsonc, which the CLI
also reads. User-level settings rank below that file.
| Setting |
|
prairielearn-navigator.plVersion |
latest (default), a date (YYYY-MM-DD), or a commit sha |
prairielearn-navigator.schemaCacheDir |
Schema cache, relative to the workspace folder. Default: the extension's storage |
prairielearn-navigator.uuidCommand |
Shell command for UUID completions. It must print one UUID. Default: uuidgen |
prairielearn-navigator.rules |
Rule id → off / info / warning / error |
prairielearn-navigator.diagnostics.<rule-id> |
Dedicated severity dropdown for every built-in rule: default / off / info / warning / error. A non-default named setting overrides the rules object. |
prairielearn-navigator.excludes |
Glob patterns of files never to check, relative to the workspace folder. Adds to the course file's excludes rather than replacing them. |
The older unusedQuestions, unusedTags, unusedAssessmentSets, and
missingQuestionInfo setting names remain supported as aliases. The uniform
diagnostics.* setting takes precedence when both specify a severity in the
same scope.
Every PrairieLearn Navigator diagnostic offers Ignore this violation in this
file, which adds a suppression to .pl-navigator.jsonc, and Configure
severity for …, which opens that rule's dropdown in VS Code Settings.
Suppressions use semantic identifiers when available—question ids, tag names,
and assessment-set names—and fall back to exact message/occurrence matching.
Unused tags and assessment sets can be removed directly; an unused question
can be moved to Trash after confirmation.
Run PrairieLearn: Reload Schemas and Config to retry downloads after going
back online.
CLI
The extension's diagnostics are also published as a CLI,
@sybelblue/prairielearn-navigator, so a course
repo can fail CI on them:
npx -y @sybelblue/prairielearn-navigator check --format github
To update references after explicitly renaming one or more QIDs, preview the
changes first and then apply them with --write:
npx -y @sybelblue/prairielearn-navigator rename-qids old new topic/one unit/one
npx -y @sybelblue/prairielearn-navigator rename-qids --write old new topic/one unit/one
This command changes semantic QID references only; it never moves question
directories or infers moves made by Git or shell tools.
See packages/cli/README.md for options and a GitHub
Actions example.
The npm package also has a typed, headless
createQuestionFileResolver API for
applications that need normalized question-local, inherited-template, and
course-level file usage without invoking the CLI.
Development
Common commands are in the Makefile. Run make help to list them:
make install # npm ci
make test-all # lint, CLI tests (Vitest), extension tests (VS Code)
make package # build a .vsix
make check ARGS="path/to/course" # run the CLI against a course
To try every feature, run the Run Extension (Demo Course) launch
configuration (F5 in the Run and Debug view). It opens
demo/course, a small course with deliberate problems, and its
TOUR.md. This repo's own
.vscode/settings.json excludes demo/ and the
broken test fixtures, so they don't fill the Problems panel while you work on
the extension itself.
Adding a rule
Other rules live in src/core/rules/registry.ts. Each
rule id maps to an ordered list of [plVersionRange, courseRelativeGlob, impl]
entries, and for each file the first entry that matches both runs.
Version ranges compare dates, e.g. ">=2025-04-17". When PrairieLearn changes,
add a newer entry above the old one instead of editing it. Also add a new rule
id to schemas/pl-navigator.schema.json; a
test checks the two agree.
Every JSON field and element attribute that names a file is one entry in
src/core/references/specs.ts. Each entry gets
its existence rule, jump-to-file, and uses from that table, so adding a
reference is one entry there plus its rule id in the config schema. Tests
check every entry has a registered rule, a schema entry, and a fixture.
Releasing
The extension and the npm CLI (@sybelblue/prairielearn-navigator) share one
version. To release, put your notes under ## [Unreleased] in
CHANGELOG.md, then run:
make publish patch # or: minor / major
This:
- Checks you're on a clean
main that matches origin/main, and runs lint
and all tests.
- Bumps both
package.json files, dates the CHANGELOG entry, commits, tags
vX.Y.Z, and pushes.
- Creates the GitHub release, which runs
publish-npm.yml to stage the CLI on
npm (with provenance), and waits for that run.
- Opens your npmjs.com Staged Packages page so you can review and approve the
version with 2FA. The script then waits for
quit or check published;
the latter verifies the version is public before continuing.
- Publishes the extension with
vsce publish, only once the npm version is
public.
Use make publish patch DRY_RUN=1 to run the checks and print the release
steps without changing anything.
If a step after the push fails, the tag and GitHub release already exist but
the extension isn't published. Fix the problem (for a failed workflow, rerun it
with gh run rerun <id>), then run make publish-vscode from the release
commit. It opens the staged npm version for approval if needed, then publishes
the extension after you confirm npm has made the version public. If you chose
quit at the npm prompt, use this command when you're ready to resume; it
reopens the Staged Packages page unless the version is already public.
One-time setup:
gh auth login
- Sign in to npmjs.com with 2FA enabled, needed to approve staged publishes
npx vsce login sybelblue, using a Marketplace personal access token
- On npmjs.com, the package's trusted publisher must be GitHub Actions,
SybelBlue/prairielearn-navigator, workflow publish-npm.yml, environment
npm, with stage-publish permission.