A tool for building and visualizing program roadmaps. You break a program down into projects → milestones → increments → initiatives, apply reusable sizing keys (for example S/M/L mapped to a number of weeks per phase), and the tool generates a Gantt-style timeline.
It runs entirely on your own laptop. Your data is stored in a local file on your machine and is never uploaded anywhere.
This README covers installing and running the app. For how to actually use it once it's running — building a roadmap, sizing work, and reading the timeline — see the User Guide.
- Organize a program into projects, milestones, increments, and initiatives
- Drag and drop to reorder work
- Define reusable sizing keys so estimates stay consistent across projects
- Size initiatives separately for policy work and implementation work
- Generate a visual timeline (Gantt chart) with configurable units
- Set a program start date and sprint cadence
- Add named date markers (such as a target launch) as vertical lines on the timeline
- Zoom in and out to change how much of the timeline is visible at once
- View a combined timeline across multiple projects
No coding required. You will copy and paste a few commands into an app called Terminal. Total time is about 10 minutes the first time, and about 10 seconds every time after that.
You need access to this private repository. If you cannot open https://github.com/navapbc/program-roadmap-generator in your browser, sign up for a GitHub account and submit an Eden ticket to get added to the NavaPBC GitHub organization first. Everything below will fail without that access.
On your Mac, press Cmd + Space, type Terminal, and press Enter. A window with a text prompt
will open.This is where you will paste the commands below.
For each step: copy the command, paste it into Terminal, press Enter, and wait for it to finish
before moving to the next one. A step is finished when you see the prompt (a line ending in % or $)
appear again.
This installs Homebrew (a software installer for Mac), then Node.js (which runs the app) and the GitHub command line tool (which downloads the code).
Install Homebrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"You will be asked for your Mac password. As you type it, nothing will appear on screen — that is
normal. Type it and press Enter. At the end, Homebrew may print two extra commands starting with
echo and ask you to run them. If it does, copy and run them.
Then install Node.js and the GitHub tool:
brew install node@22 ghVerify it worked:
node --versionYou should see a version number starting with v22 (for example v22.23.2). If you see
"command not found", close Terminal, open it again, and re-run the command.
gh auth loginAnswer the prompts with the arrow keys and Enter:
- What account do you want to log into? →
GitHub.com - What is your preferred protocol? →
HTTPS - Authenticate Git with your GitHub credentials? →
Yes - How would you like to authenticate? →
Login with a web browser
Terminal will show a one-time code such as A1B2-C3D4. Copy that code, press Enter to open your
browser, paste the code, and approve access. When your browser says you are done, return to Terminal
and wait for ✓ Logged in as ....
Then run this once so downloads work:
gh auth setup-gitcd ~ && gh repo clone navapbc/program-roadmap-generator && cd program-roadmap-generatorThis puts the app in a folder called program-roadmap-generator in your home directory.
npm run setupThis installs everything, creates your local database, and loads sample data. It takes a few
minutes and prints a lot of text — that is expected. You are done when you see Seed complete.
You only ever need to run this once.
npm run devWait until you see a line like:
[web] ➜ Local: http://localhost:5173/
Now open http://localhost:5173 in your browser. You should see a project list containing a project called Sample Program.
That's it — you're running the tool.
Next step: the User Guide walks through building your first roadmap. Start with
Your first 15 minutes, and explore the Sample Program
project as you read — it's placeholder data, so you can't break anything.
To stop the app: click on the Terminal window and press Ctrl + C. Closing the browser tab does
not stop it. It is safe to leave it running.
To start it again later, open Terminal and run these two commands:
cd ~/program-roadmap-generator
npm run devYou do not need to repeat steps 2 through 5.
To get the latest version of the app after someone makes updates:
cd ~/program-roadmap-generator
git pull
npm run setupYour projects are saved in a single file on your laptop at
apps/server/prisma/dev.db. This means:
- Your data is private and stays on your machine
- It is not backed up, and it is not shared with teammates
- If you delete the app folder, your projects are gone
You can back up the project directory, including the database file, with Google Drive Sync if you choose.
The sample data is placeholder content, so feel free to delete the Sample Program project once
you have created your own. Note that deleting a project cannot be undone — see
Things that surprise people in the User Guide for the
other places the app doesn't ask twice.
To wipe everything and start over with fresh sample data:
npm run db:resetBy default the app uses address 5173 for the page you open, and 4000 behind the scenes. If either is
already taken — often because you are running a second copy of this app — open the file named .env
in the program-roadmap-generator folder and change these two lines to any numbers between 1024 and
65535:
API_PORT=4400
WEB_PORT=5273
Save the file, stop the app with Ctrl + C, and start it again with npm run dev. You would then
open http://localhost:5273 instead. No other changes are needed.
"command not found: brew" (or node, or gh)
Close Terminal completely and open a new window, then try again. Installers often only apply to
newly opened windows.
"address already in use" or EADDRINUSE
The app is already running in another Terminal window. Switch to that window and press Ctrl + C,
or close all Terminal windows and start again. This matters: the web page may still load while the
data service is not actually running, which makes the app look broken.
If something unrelated is using those ports and you cannot stop it, change the ports instead — see "Changing the ports" below.
The page loads but is blank, or shows errors about loading data
The web page and the data service are two separate pieces, and both must be running. Look at the
npm run dev output — you should see both [server] and [web] lines with no errors. If only
[web] started successfully, see the "address already in use" fix above.
Browser says "This site can't be reached"
The app isn't running. Run npm run dev and wait for the Local: http://localhost:5173/ line
before opening the browser.
"@prisma/client did not initialize yet"
Run npm run setup again from the program-roadmap-generator folder.
Terminal opened somewhere unexpected / commands can't find files
Run cd ~/program-roadmap-generator first. Every command in this guide assumes you are in that
folder.
"Authentication failed" when downloading
Run gh auth status to confirm you are signed in, and gh auth setup-git to link Git to your
GitHub account. If it still fails, confirm you have access to the NavaPBC organization.
Still stuck? Copy the last 20 lines of red or error text from Terminal and send them to the repo owner. The exact error text is the useful part.
The problems above are all about getting the app running. If the app is running but not behaving how you expect — a greyed-out sizing key, missing timeline rulers, a schedule that got shorter — see Common questions in the User Guide instead.
The User Guide documents the domain model and product behavior from a user's perspective — the final-size calculation, the sizing-key compatibility rule, sequential scheduling, and calendar-accurate month durations. It's the quickest way to get oriented before reading the code.
npm workspaces monorepo:
apps/server— Fastify + tRPC API, Prisma ORM over SQLite. Listens onAPI_PORT(default 4000).apps/web— React 18 + Vite + Tailwind + TanStack Query. Serves onWEB_PORT(default 5173) and proxies/trpctoAPI_PORT(seeapps/web/vite.config.ts).packages/shared— types, Zod schemas, and sizing/timeline logic shared by both, tested with Vitest.
Ordering of milestones, increments, and initiatives uses fractional indexing (orderKey) so drag
and drop reordering only writes a single row.
Project → Milestone → Increment → Initiative, with Project.sizeLabels defining the size codes
available to initiatives. Each initiative can carry both a policy size and an implementation size.
SizingKey is a reusable estimation template: SizingKeyLabel defines the codes (S/M/L), and
SizingPhase + SizingDuration map each code to a duration per phase. A project references one
via defaultSizingKeyId.
Run from the repo root:
npm run setup— install, create.env, migrate, generate the Prisma client, and seednpm run dev— run server and web togethernpm run build— build all workspacesnpm test— run the shared package's Vitest suitenpm run seed— reseed placeholder datanpm run db:reset— drop the database, re-migrate, and reseed
In apps/server:
npm run migrate— create a new migration (prisma migrate dev)npm run studio— browse the database in Prisma Studio
A single .env at the repository root configures everything. npm run setup creates it from
.env.example:
DATABASE_URL— SQLite path, resolved relative toapps/server/prisma/API_PORT— port the API listens on, defaults to4000WEB_PORT— port the web app serves on, defaults to5173
How each piece reads it:
- The server loads it explicitly in
apps/server/src/env.ts, which must be imported first in any entry point because Prisma Client readsDATABASE_URLwhen it is constructed.index.tsandprisma/seed.tsboth do this. - Vite reads it with
loadEnvinapps/web/vite.config.tsto set the dev server port and proxy target. Keys are read without theVITE_prefix and are never exposed to the browser. - Prisma CLI commands are wrapped in
dotenv -e ../../.envbecause Prisma only looks for.envbeside its schema, not at the repo root.
The file is authoritative: env.ts loads it with override: true, so values in .env win over
variables already set in the shell. To use a different port, edit .env rather than exporting a
shell variable.
.env files and *.db files are gitignored and must never be committed.
cd apps/server
npm run migrate -- --name your_change_nameUse the migrate script rather than calling npx prisma directly — the script wraps Prisma in
dotenv -e ../../.env so it picks up the root config.
Commit the generated folder under apps/server/prisma/migrations/.