From pulp
Guides users through upgrading the Pulp CLI, interpreting migration notes, and applying breaking-change fixes. Handles 'upgrade pulp', 'what's new', and '/upgrade'.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pulp:upgradeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- User asks to upgrade, update, or bump the Pulp CLI.
/upgrade in Claude Code.pulp doctor --versions run surfaces CLI-vs-SDK skew the user wants
to resolve.This skill does NOT cover:
cli-maintenance skill + pulp project bump.pulp version bump plus the release workflow.ci skill via pulp pr.CLAUDE.md (runs through tools/deps/audit.py).Four independently-versioned surfaces, two of which this skill acts on
directly and one of which it may hand off to pulp project bump:
| Surface | Source of truth | How to upgrade |
|---|---|---|
| Pulp CLI / SDK | ~/.pulp/bin/pulp (installed binary) | pulp upgrade |
| Consumer project SDK pin | project pulp.toml sdk_version + find_package(Pulp X.Y.Z ...) | pulp project bump |
| Pulp source checkout version | Pulp repo CMakeLists.txt / release metadata | pulp version bump + release workflow |
| Claude plugin | .claude-plugin/plugin.json | /plugin install pulp in Claude Code |
| Shipyard pin | tools/install-shipyard.sh | Dependency Update Workflow (out of scope) |
pulp upgrade downloads the new CLI binary and replaces the installed
one. After the Phase 8 Rust cutover, release archives are dual-binary:
Rust pulp is the user-facing CLI and sibling pulp-cpp is installed
as the C++ fallthrough delegate. A healthy install has both
~/.pulp/bin/pulp and ~/.pulp/bin/pulp-cpp; use PULP_USE_CPP=1 pulp <args> or direct pulp-cpp <args> only for rollback/debug
comparisons. Do not hand-swap binaries in user/system locations.
The C++ cmd_upgrade.cpp path still matters for pre-cutover users
upgrading into a Rust release: it must copy the archive's sibling
payloads, including pulp-cpp, before replacing the running pulp
binary.
pulp upgrade --notes prints migration notes for the hop without
downloading anything. pulp upgrade --notes --json emits the same data
as a stable-shape JSON document for agent consumption (the /upgrade
slash command is the primary consumer).
Use pulp project bump only after deciding a consumer project should
move its SDK pin. It operates on the active project or the registry
(--all); it does not upgrade the global CLI and it refuses to treat
the Pulp source checkout as a consumer project.
pulp upgrade updates BOTH CLI and SDK by defaultOlder pulp upgrade releases only swapped the CLI binary. Users who ran them
then built a project that still resolved its months-old SDK and silently missed
framework fixes. Current behavior:
pulp sdk install --version <new> immediately after the swap, so the matching SDK lands at
~/.pulp/sdk/<new>/ in the same invocation.--cli-only flag keeps the CLI-only behavior for the rare case
where a user wants the new CLI paired with their current SDK.pulp sdk install. The CLI
swap is not rolled back — the user can always retry SDK install.pulp.toml pins an explicit
SDK version, the SDK install happens (so the new version is
available globally) but the project's pin is left ALONE. A clear
notice prints — "Project X stays on pinned SDK Y.Y.Y; latest
available is Z.Z.Z — pulp project unpin to start tracking latest."
Pinning is sacred when the user has explicitly pinned.pulp project pin <version> is the new primary name for what
pulp project bump did. bump survives as a deprecated alias for
one minor release. New docs and skill examples should use pin.
pulp project unpin (new) flips a project back to floating mode
(sdk_version = "latest" in pulp.toml).
New projects created via pulp create default to floating mode
(sdk_version = "latest"), so they pick up framework fixes
automatically. pulp create --pin writes the exact version instead
for users who want reproducibility from day one.
pulp on PATH is assumed)The skill shells out to pulp — no hardcoded paths, no env-var
requirement. If pulp is not on PATH, tell the user to install first
(curl -fsSL https://www.generouscorp.com/pulp/install.sh | sh)
and stop.
Note: a successful pulp upgrade now self-heals PATH — after the
binary swap it appends the CLI's own directory to the user's shell
profile when it isn't already on $PATH (honoring PULP_NO_MODIFY_PATH).
This closes the gap where a CLI first installed via a source / SDK-prefix
install (cmake --install --prefix ~/pulp-sdk → ~/pulp-sdk/bin/pulp)
could upgrade successfully yet still be "command not found" in a fresh
shell. So after an upgrade the user may need to restart their shell or
source the named profile, but no longer has to add PATH by hand. See
the cli-maintenance skill for the implementation
(upgrade_install::ensure_dir_on_path).
Before running any pulp command, source the shared skew-check helper
so the user sees a single-line hint when the installed CLI is older
than the plugin's declared min_cli_version:
source "$(git rev-parse --show-toplevel 2>/dev/null || echo .)/tools/scripts/cli_version_check.sh"
pulp_cli_version_check # no-ops silently if already checked this session
Behaviour:
[pulp] Claude plugin requires CLI >= v<MIN> but installed CLI is v<HAVE>. Run \pulp upgrade` or `/upgrade` in Claude Code.`min_cli_version (older plugin builds), or when either version is
non-numeric (dev builds).PULP_SKEW_CHECK_DISABLE=1 turns it off;
PULP_SKEW_CHECK_CACHE overrides the session-marker directory.The same skew logic is surfaced by pulp doctor --versions inline, so
users who prefer running the diagnostic directly see the finding there
too — the helper is a convenience for skill authors, not a second
source of truth.
Identify the active plugin directory (so the skill can resolve
docs/migrations/ for full-body lookups):
pulp doctor --versions --json
The JSON output contains plugin_json_path — walk up two levels
(dirname $(dirname $plugin_json_path)) to reach the plugin root.
Report the version skew at a glance so the user sees where they are
before deciding what to do. The JSON response also includes cli,
plugin, project_sdk, and findings[] — surface those in a
compact table.
Fetch applicable migration notes for the current hop.
Important: user-invoked pulp upgrade and pulp upgrade --install force a synchronous release refresh instead of trusting
the 24h cache. This is deliberate: v0.78.3 showed that a cache
written minutes before a release can make a just-published version
invisible. pulp upgrade --check-only remains the lightweight
cache-aware probe; if you need release-fresh notes, capture the
Latest: value from a refreshed/default pulp upgrade run and
forward it as --to "$LATEST".
In CI / sandbox lanes, PULP_UPDATE_CHECK_DISABLED=1 makes
--check-only network-free. If the cache is empty in that mode, the
command reports the installed CLI version plus an explicit
disabled/not-queried latest line instead of querying GitHub Releases.
pulp upgrade --notes --json # defaults: from = installed CLI, to = cached latest
pulp upgrade --notes --json --to "$LATEST" # recommended — use value captured from refreshed upgrade check
pulp upgrade --notes --json --from X --to Y # explicit hop
When verifying a newly published release, test from the previously published CLI with a deliberately fresh cache that still names the old release:
PULP_HOME="$(mktemp -d)" pulp upgrade --check-only --json
# seed/update-cache.json to latest_version=<previous> with a current timestamp
pulp upgrade
The default pulp upgrade path must refresh through GitHub and report
the new tag immediately. Then run pulp upgrade --install --to X.Y.Z
in an isolated install directory and confirm a follow-up pulp upgrade
reports both Installed and Latest as the new version. This catches stale
release-cache failures before the release is announced.
Stable-shape output (do NOT rename these keys — they are a public
surface the skill depends on, see tools/cli/migration_index.hpp):
{
"from": "0.27.0",
"to": "0.30.0",
"entries": [
{
"version": "0.28.0",
"breaking": false,
"summary": "…",
"applies_if":"cli_version_from < 0.28.0 && cli_version_to >= 0.28.0",
"body": "…"
}
]
}
breaking == true, prefix with a loud tag (e.g. "BREAKING").summary on the first line.body verbatim — it's already Markdown.Each note describes one release's visible behaviour shift. Read them like a pro-developer changelog, not a marketing blurb.
breaking: true — requires user action: code change, config edit,
habit change. Do not skip. Offer to grep the project for the affected
symbols as a follow-up.breaking: false — informational. The upgrade works without
intervention, but something worth knowing has changed.applies_if expressionsThe filter runs in the CLI — by the time the skill receives JSON,
non-applicable entries are already stripped. You'll only see entries
whose applies_if matched the hop. Still: print the expression alongside
the note so a curious user can verify why it applied.
Grammar: Boolean combinations (&&, ||, parentheses) over comparisons
of cli_version_from / cli_version_to against a literal semver. Six
operators: <, <=, >, >=, ==, !=. See
docs/migrations/README.md for full details.
These are the patterns that have shown up in Pulp migration notes. When you spot one, suggest the exact grep / replacement to the user instead of leaving them to figure it out from a prose paragraph.
Pattern. A pulp_add_* function in tools/cmake/Pulp*.cmake is
renamed, folded into pulp_add_plugin(FORMATS ...), or split out of
one macro into several.
What to do.
# Find every call site.
grep -rn "pulp_add_ios_auv3\|pulp_add_auv3\|pulp_add_vst3_only" .
Propose the replacement form explicitly; do NOT rewrite the user's CMakeLists.txt without confirmation. For unified-form migrations, show a diff like:
- pulp_add_ios_auv3(MyPlugin …)
+ pulp_add_plugin(MyPlugin FORMATS AUv3 …)
Pattern. A public C++ header under core/*/include/pulp/** adds,
removes, or renames a symbol that processors / view code use.
What to do.
# Find call sites of the old symbol.
grep -rn "OldProcessorAPI\|deprecated_function_name" .
Read the migration body for the replacement signature. If the change is a deprecation (old API still works, emits a warning), flag that — teams may want to migrate at their own pace.
Pattern. A file Pulp reads (e.g. ~/.pulp/config.toml,
pulp.toml, .pulp/projects.json) changes location or key shape.
What to do. Most config moves are handled by the CLI on first run
(it migrates the old location to the new one and prints a one-line
notice). The note tells you whether manual action is needed. If it is,
quote the exact pulp config set / pulp config unset commands from
the body.
Pattern. A subcommand is renamed (e.g. pulp check → pulp doctor),
a flag changes shape (--sign-key → --ed25519-key), or an exit code
semantic changes (for example, silent-success → loud-failure).
What to do. If the user has CI scripts (ci/, .github/workflows,
tools/local-ci/, team-specific shell scripts), grep those for the
old invocation first — those are the scripts that'll silently break.
grep -rn "pulp <old-command>\|--<old-flag>" ci/ .github/ tools/ || true
This flow does not add new bypass trailers, but users will ask about trailers
around upgrade-triggered PRs, so keep
the syntax handy (full table in CLAUDE.md → "Versioning & Skill-Sync
Policy"):
| Gate | Trailer (tip commit only, NEVER PR body) |
|---|---|
| Version bump | Version-Bump: <surface>=<patch|minor|major|skip> reason="..." |
| Skill update | Skill-Update: skip skill=<name> reason="..." |
| Auto-release | Release: skip reason="..." |
Trailer blocks must be contiguous — no blank lines between
trailers, or git interpret-trailers --parse stops reading. When
amending, rewrite the whole block, don't append.
/upgrade slash command)pulp doctor --versions --json → parse cli / plugin / project_sdk / findings
pulp upgrade --notes --json → parse entries[]
present table: CLI vX.Y.Z → vA.B.C (<hop>)
plugin vP.Q.R → vP'.Q'.R' (if drift)
project SDK vS.T.U (skew warn from findings)
list applicable notes (inline, breaking flagged)
ask via AskUserQuestion:
- "Upgrade CLI + bump project SDK" → `pulp upgrade` + `pulp project bump` in the active project
- "Upgrade CLI only" → `pulp upgrade`
- "Bump project SDK only" → `pulp project bump` in the active project or `--all` when explicitly requested
- "Dismiss" → no action, remind user they can re-run `/upgrade`
The /upgrade command shells out to pulp upgrade --notes --json.
A regression test lives in test/test_cli_shellout.cpp (the
[issue-549] tag) — it asserts that a synthetic hop produces the keys
the slash command depends on. If you add or rename a key in
render_notes_json (tools/cli/migration_runtime.cpp), update this
skill AND the slash command AND the test in the same PR.
planning/release-discovery-ux-design-2026-04-20.md Section Ctools/cli/cmd_upgrade.cpp, tools/cli/migration_index.hppdocs/migrations/README.mdpulp doctor --versionspulp upgrade --check-onlypulp upgrade --notes --jsonmin_cli_version,
tools/scripts/cli_version_check.sh, and the plugin_min_cli JSON fieldnpx claudepluginhub danielraffel/pulp --plugin pulpGuided update assistant that reads the installed SDLC version, shows changelog, displays file diffs, and lets the user selectively adopt changes while preserving customizations. Also checks the npm CLI version and offers to bump it.
Automates semantic version bumps across plugin.json, marketplace.extended.json, and marketplace.json in Claude Code plugins. Triggers on mentions of version bump, update version, or release for consistency.
Guides framework and language migrations: version upgrades, breaking changes, dependency audits, codemods, rollbacks. For React 19, Next.js App Router, Python 3.12, Node 22, etc.