Make PromptKit version visible: session banner + CLI update check#249
Make PromptKit version visible: session banner + CLI update check#249
Conversation
Add an instruction to bootstrap.md step 1 so the composition engine emits a one-line 'PromptKit v<version> loaded.' banner after reading manifest.yaml. The version is read from the top-level 'version:' field; a 'version unknown' fallback is used if the field is missing or unreadable, and no version is ever fabricated. This applies to both manual loads and the 'promptkit interactive' (npx) flow, since the CLI stages the same bootstrap.md and manifest.yaml into a temp dir before instructing the LLM to read bootstrap.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a version-identifying banner to PromptKit’s bootstrap flow so every interactive session immediately reports which PromptKit release is loaded, improving provenance for debugging and support.
Changes:
- Update
bootstrap.md“How to Begin” step 1 to instruct the engine to readmanifest.yaml.versionand emit a one-line “PromptKit vX loaded.” banner (with an “unknown” fallback). - Re-announce the version when
bootstrap.mdormanifest.yamlis re-read later in the session.
Adds a best-effort daily update check to 'promptkit interactive'. The CLI queries https://registry.npmjs.org/<pkg>/latest (built-in https, ~1500ms timeout), caches the result in ~/.promptkit/update-check.json for 24h, and prints a boxed banner before spawning the LLM when a newer version exists. No new runtime dependencies. Suppressed by NO_UPDATE_NOTIFIER=1, CI, non-TTY stdout, --no-update-check, and for non-interactive subcommands (list/search/show/--version). Network, cache, and parse failures are silently swallowed and never surface to the user. Adds cli/tests/update-check.test.js with unit coverage for parseVersion, isNewer, formatBanner, and suppressionReason (no network I/O). Updates cli/tests/cli.test.js harness to copy the new lib/update-check.js into the temp CLI root. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| const req = https.get( | ||
| url, | ||
| { timeout: FETCH_TIMEOUT_MS, headers: { Accept: "application/json" } }, | ||
| (res) => { |
There was a problem hiding this comment.
FETCH_TIMEOUT_MS is described as a hard ~1500ms cap, but https.get({ timeout }) only sets a socket inactivity timeout (it can exceed 1500ms if the server trickles data). If you need a true hard deadline, add an explicit overall timer (e.g., setTimeout that destroys the request) so interactive startup can’t be delayed longer than intended.
| latest = await fetchLatest(pkgName); | ||
| if (latest) { | ||
| writeCache({ pkg: pkgName, latest, checkedAt: now }); | ||
| } |
There was a problem hiding this comment.
checkForUpdate caches any truthy latest string, even if it’s not parseable by parseVersion. That can lock users into a bad cached value for 24h (and can also lead to odd banner output if it’s unexpectedly formatted). Consider validating latest with parseVersion (or isNewer inputs) before writing it to cache / returning it.
| const cache = readCache(); | ||
| let latest = null; | ||
|
|
||
| if ( | ||
| !force && | ||
| cache && | ||
| cache.pkg === pkgName && | ||
| typeof cache.latest === "string" && | ||
| typeof cache.checkedAt === "number" && | ||
| now - cache.checkedAt < CACHE_TTL_MS | ||
| ) { | ||
| latest = cache.latest; | ||
| } else { | ||
| latest = await fetchLatest(pkgName); | ||
| if (latest) { | ||
| writeCache({ pkg: pkgName, latest, checkedAt: now }); | ||
| } | ||
| } |
There was a problem hiding this comment.
The cache/TLL branch in checkForUpdate (reading ~/.promptkit/update-check.json, TTL gating, and the “cached newer version still shows banner” behavior) isn’t covered by unit tests yet. Since _internals are already exported, consider adding tests that stub cache read/write + now to exercise: fresh cache hit, expired cache triggering fetch, and banner decision based on cached latest.
Closes #248.
Closes #250.
This PR adds two small, complementary pieces of version visibility to PromptKit.
1. Announce the loaded PromptKit version (#248)
One surgical edit to
bootstrap.mdstep 1 ofHow to Begin. After readingmanifest.yaml, the composition engine now reads the top-levelversion:field and emits a banner before any other output. If the field is missing or unreadable, a(version unknown)fallback is emitted — no version is ever fabricated. The engine re-announces wheneverbootstrap.mdormanifest.yamlis re-read.All three entry-point skills (
/promptkit,/bootstrap,/boot) and thepromptkit interactive(npx) flow converge onbootstrap.md, so this one edit covers every activation path — manual load, slash command, and CLI.New session-start output
Against the current repo (
manifest.yamlhasversion: ""0.6.1""), the first line of any new session is:If
manifest.yamlever ships without a readableversion:field, the engine emits instead:2. Notify users when a newer CLI version is available (#250)
Adds a best-effort update check to
promptkit interactive. Follows the standard pattern used bynpm,yarn,vercel, andgh, but implemented with no new runtime dependencies (pure built-inhttps).Behavior
Queries
https://registry.npmjs.org/@alan-jowett/promptkit/latestwith a hard ~1500 ms timeout.Compares
json.versionagainstpackage.json.versionwith simplemajor.minor.patchordering (strips a leadingv, ignores prerelease/build suffix).On newer version, prints a boxed banner before spawning the LLM CLI:
Caches the result in
~/.promptkit/update-check.jsonfor 24 h; subsequent launches skip the network but still show the banner if a newer version is cached.Network, DNS, timeout, HTTP-non-200, malformed-JSON, and cache I/O failures are all swallowed — the check is strictly best-effort and can never block or break the CLI.
Response body is capped at 64 KB to defend against a misbehaving registry.
Opt-out / non-interactive safety
NO_UPDATE_NOTIFIER=1— suppresses the check.CIenv var set — suppresses.stdoutis not a TTY (piped/scripted usage) — suppresses.--no-update-checkflag — suppresses.list,search,show,--version) never run the check, keeping machine-readable output clean.Surface area
cli/lib/update-check.js— new module. Pure functions (parseVersion,isNewer,formatBanner,suppressionReason) plus the one network function (fetchLatest) behindcheckForUpdate.cli/bin/cli.js—interactiveaction becomesasync, callscheckForUpdateinside a try/catch beforelaunchInteractive, gated by the new--no-update-checkflag.cli/tests/update-check.test.js— new unit tests for version parsing/comparison/banner/suppression. No network I/O in tests.cli/tests/cli.test.js— test harness extended to copylib/update-check.jsinto its temp CLI root.cli/package.json—testscript adds the new test file. No new dependencies.Test status
npm testincli/passes 65/66. The one remaining failure (TC-CLI-082/083 gh-copilot spawned with originalCwd and --add-dir for staging dirinlaunch.test.js:335) also fails onmainand is unrelated to this change — it is a pre-existing flaky/environmental test.python tests/validate-manifest.py— OK.Version-source consistency
manifest.yaml.version(what the in-session banner reads) andcli/package.json.version(whatpromptkit --versionand the update check read) are already kept aligned percli/specs/design.mdv0.6.1, so all three user-visible version surfaces report the same number for any published release. A follow-up to enforce this intests/validate-manifest.pyis captured in #248.Files changed
bootstrap.md(+7 lines)cli/bin/cli.js(+14 / -1)cli/lib/update-check.js(new, 176 lines)cli/package.json(+1 / -1 — test script)cli/tests/cli.test.js(+6 lines — harness)cli/tests/update-check.test.js(new, 152 lines)No persona, protocol, template, format, or taxonomy content touched.