programmapper-cli is a Go CLI + MCP server that mirrors California Community College "Program Pathways Mapper" catalogs (degrees, courses, transfer paths, careers) into local SQLite and exposes scriptable, agent-native commands (search, plan, compare, diff-years, bottlenecks) that the click-heavy web app can't. The catalog data is public — no API key.
This tree is generated output from CLI Printing Press. Prefer fixing systemic issues upstream in Printing Press; keep any local edit narrow and recorded (see Gotchas).
- Entry points:
cmd/programmapper-cli/main.go(the CLI) andcmd/programmapper-mcp/main.go(the MCP server, published binaryprogrammapper-mcp). Both are thin mains overinternal/. internal/packages:cli/— cobra commands, one file per command (colleges_*.go,plan,compare,bottlenecks,search,mirror,doctor,agent_context.go,which, …) plus*_test.go.client/— HTTP client for the ProgramMapper API. Uses a Chrome-fingerprinted transport (github.com/enetx/surf/enetx/http) to get past the WAF; not a resident browser.store/— local SQLite catalog viamodernc.org/sqlite(pure-Go, no cgo). Populated bymirror.cache/,config/,cliutil/,types/,mcp/— HTTP cache, path/config resolution, shared CLI helpers, shared types, MCP wiring.
- Data flow:
mirror <college>fetches programs/maps/courses intodata.db; offline commands (plan,compare,course-programs,bottlenecks,search --data-source local) read from that SQLite store. - Path kinds: config / data / state / cache each resolve independently (per-kind env var →
--home→PROGRAMMAPPER_HOME→ XDG → platform default). See README "Paths & environment variables".
make build # go build -o bin/programmapper-cli ./cmd/programmapper-cli
make build-mcp # go build -o bin/programmapper-mcp ./cmd/programmapper-mcp
make build-all # both binaries
make test # go test ./...
make lint # golangci-lint run
make install # go install ./cmd/programmapper-cli
# Runtime discovery (prefer over a memorized command list):
./bin/programmapper-cli doctor --json
./bin/programmapper-cli agent-context --pretty
./bin/programmapper-cli which "<capability>" --json
./bin/programmapper-cli <command> --help- Agent-first CLI: non-interactive, every input is a flag,
--jsonto stdout / errors to stderr.--agentbundles JSON + compact + no color + non-interactive. Use--dry-runbefore anything that hits the network;--yes --no-inputonly once target and side effects are clear. - Exit codes:
0ok,2usage,3not found,5API error,7rate limited,10config error. - Lint: golangci-lint with
errorlint, govet, staticcheck, unused, bodyclose, noctx, rowserrcheck, sqlclosecheck; formattersgofmt+goimports(see.golangci.yml). Runmake lintbefore finishing. - Go 1.26. One cobra command per file in
internal/cli; add a matching*_test.go.
- Generated tree. A fresh print can overwrite this whole directory. Ad-hoc hand-edits do not survive on their own — record every intentional change under
.printing-press-patches/(parallel to.printing-press.json) so a regen carries it forward. - Do not hand-bump the release ledger.
CHANGELOG.md,.printing-press-release.json, andvar version = …are stamped by themvanhorn/printing-press-librarypublish workflow (finalYYYY.M.Nassigned on merge). Preserve those files on reprint; never edit them for bookkeeping. - API is WAF-guarded. Bursts get HTTP 403 / rate-limited per IP; the client paces requests — retry after a pause, don't crank concurrency.
- Offline commands need a mirror first. "no local mirror" / empty results ⇒ run
mirror <college>. colleges resolveneeds the full URL including scheme (https://la-mission.programmapper.ws).*.db,*.db-shm,*.db-wal,/build/, and the built binaries are git-ignored (regenerable) — don't commit them.- Releases are cut by pushing a
v*tag (GoReleaser,.github/workflows/release.yml);.goreleaser.yamlis the config.