Skip to content

Check that each skill is up to date on first use - #26

Merged
njb90 merged 13 commits into
mainfrom
feat/version-check
Nov 4, 2026
Merged

njb90 merged 13 commits into
mainfrom
feat/version-check

Conversation

@njb90

@njb90 njb90 commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Most people install skills once and never update them. With this change, each skill checks whether it's up to date the first time it's used in a conversation, and tells the user when a newer version is available.

How it works

Each SKILL.md gets a short "Before you start: check this skill is current" section directly under its H1. The first time the skill is used in a conversation, the agent:

  1. Fetches the Cloudinary skills index at https://cloudinary.com/documentation/.well-known/agent-skills/index.json. This is the Agent Skills discovery index, which the skills CLI can also install from.
  2. Compares the published x-cloudinary-version for the skill with the installed version.
  3. If the published version is newer, tells the user in one line that they can update with npx skills update <name>, or reinstall with npx skills add https://cloudinary.com/documentation. Otherwise it carries on.

Things it doesn't do:

  • Run the update itself.
  • Repeat the check within the same conversation.
  • Retry if the fetch fails.
  • Suggest a downgrade when the installed skill is newer than the index.

Agents that can't fetch URLs skip the check. The check also follows the user's or project's instructions to skip it.

What's sent

The request includes the skill name and version, the agent product and model, the kind of task (for example setup or docs), and the user's cloud name, but only if the agent already has it from the conversation or the code it's working on. The block tells the agent never to open .env files or read CLOUDINARY_URL for this, and never to include API keys or secrets. The README documents this and how to turn the check off.

Keeping the block consistent

  • templates/version-check.md is the single source for the block.
  • scripts/check-version-block.sh --fix renders it into every skill, filling in the skill's name, version, and referrer.
  • A new CI job, version-check-block, fails if a skill's block is missing, edited by hand, misplaced, or out of date after a version bump.
  • CONTRIBUTING explains how to change the block.

Versions

Skill Before After
cloudinary-docs 1.1.0 1.2.0
cloudinary-transformations 1.0.4 1.1.0
cloudinary-react 1.0.2 1.1.0
cloudinary-next 1.0.0 1.1.0

Testing

Headless Claude Code runs, with the skills installed from this branch:

Scenario Result
Skill current (next, docs) One check each. The agent didn't mention it.
Skill out of date (react installed as 0.9.0) One check, with a correct one-line update notice
Cloud name stated in the prompt (next) One check, cloud name included
Cloud name in project code (react) One check, cloud name included
No cloud name available One check, cloud name left out

To test locally:

npx skills add cloudinary-devs/skills#feat/version-check

Then ask your agent a Cloudinary task. To see the update notice, change the version in an installed skill's SKILL.md to 0.9.0 (in both the frontmatter and the block) and start a new conversation.

Each SKILL.md now has a short "check this skill is current" section under its H1. The agent fetches the docs site's skills index once per conversation and compares the published version with the installed one. It mentions an update only when the index has a newer version, and never runs it itself. The request carries analytics query parameters (skill, version, surface, model, purpose, and the cloud name if the agent already has it from the conversation or code).

- templates/version-check.md is the single source for the block
- scripts/check-version-block.sh renders it into each skill (--fix) or checks it
- New CI job fails on a missing, edited, or misplaced block
- README discloses what's sent and how to turn the check off
- CONTRIBUTING covers editing the block and rerunning --fix after a version bump
- Minor version bump for all four skills
skills@latest (1.7.4) requires Node 22.20 or newer and crashes on Node 20.

@jackieros jackieros left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some suggestions/questions - both mine from my personal review & some that claude raised.

Comment thread templates/version-check.md Resolved
Comment thread templates/version-check.md Outdated
- `<surface>`: the product you're running in, lowercase with hyphens, for example `claude-code`, `cursor`, `codex`, `copilot`, `claude-desktop`, or `chatgpt`.
- `<model>`: your model ID if you know it, otherwise `unknown`.
- `<purpose>`: one of `setup`, `upload`, `transform`, `debug`, or `docs`.
- `<cloud name>`: only a Cloudinary cloud name you already have from this conversation or from code you've read for this task. Don't open `.env` files or read `CLOUDINARY_URL` to find one, and never include an API key or secret. If you don't have one, drop `&cloud_name=<cloud name>` from the URL.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the chances of a cloud name being discussed in a conversation except when a user is first configuring their account is close to 0. If they implemented their cld config properly, same is true for seeing a cloud name directly in code they've read for this task, since cloud usually wont be in the code anywhere except the .env file (or whatever file they use to configure the SDK). That means that I'd expect we'd almost never get account details from this check, which was the primary goal.

Slightly expanded suggestion below that hopefully increases the chances of getting a cloud name while keeping the privacy story stays pretty much the same. Open to improvements.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, agreed that "mentioned in chat" alone would almost never give us a cloud name. I took your suggestion and tweaked slightly, including the unknown addition. Still avoided doing any .env check as I think it's a security concern.

Comment thread templates/version-check.md Resolved
Comment thread scripts/check-version-block.sh Resolved
Comment thread templates/version-check.md Resolved
Comment thread templates/version-check.md Resolved
Comment thread README.md Resolved
Comment thread templates/version-check.md Resolved
Comment thread templates/version-check.md Resolved
Comment thread templates/version-check.md Resolved
@PaulBoye-py

Copy link
Copy Markdown
Collaborator

Hi @njb90 @jackieros, just an FYI: this PR is currently a blocker for #28 (the next-cloudinary v7 updates to the cloudinary-next skill).

The "Every SKILL.md is discovered" check is failing on PRs based on main, because npx skills@latest now needs Node 22.20 or later and the workflow on main still uses Node 20. This PR includes the Node 22 workflow change (57f413b), so #28 will be waiting for this one to be merged.

Once this is merged, I'll update #28 from main and bump cloudinary-next to 1.2.0, since both PRs currently bump it to 1.1.0. Nothing extra is needed on this PR for that.

Thank you!

@jackieros

jackieros commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Note on the versions CI check (scripts/check-versions.sh) from #22.

While confirming that this branch passes it, I found two gaps. Both are tracked for a separate PR in DOC-11784.

1. The check doesn't require a higher version

The check compares the PR's base and head commits and fails only when a changed skill's metadata.version is identical in both:

if [ "$bv" = "$hv" ]; then   # fail

It never checks that the new version is higher. A branch that lowers a version (for example 1.1.0 → 1.0.9), or sets one lower than what's on main, still passes.

This PR is fine: all four skills it touches move ahead of main (cloudinary-docs 1.1.0 → 1.2.0, cloudinary-next 1.0.0 → 1.1.0, cloudinary-react 1.0.2 → 1.1.0, cloudinary-transformations 1.0.4 → 1.1.0), and the check passes locally against origin/main.

Suggested fix (separate PR): replace the equality test with a semver comparison, so the check fails unless head > base. For example:

if [ "$(printf '%s\n%s\n' "$bv" "$hv" | sort -V | tail -1)" != "$hv" ] || [ "$bv" = "$hv" ]; then
  echo "::error file=$head_md,line=7::$name changed but metadata.version ($hv) is not higher than $bv. Bump it (see CONTRIBUTING.md, Versioning)."
  fail=1

2. The wording reads as "bump on every commit"

The check compares the whole branch with production, so a skill needs one bump per branch, not one per commit. The wording around it doesn't say so, and an agent working on this branch bumped versions again for a later commit, even though they were already ahead of main. The sources of the confusion:

  • CONTRIBUTING.md, Versioning: "Bump metadata.version on any content change to a skill" reads as every edit, with nothing saying the comparison is against main.
  • CONTRIBUTING.md, Version check block: "… and bump each skill's version" is unconditional.
  • The script's ok line (ok cloudinary-react: 1.0.2 -> 1.1.0) doesn't say that no further bump is needed, and the versions job name ("Changed skills bump their version") reinforces the per-change reading.

Suggested fix (same separate PR):

  • State the rule in CONTRIBUTING.md, Versioning:
    • The first change to a skill on a new branch bumps its version once: patch for a small improvement or bug fix, minor for a new feature or other significant change. Major only when a human developer explicitly asks.
    • Later commits on a branch where the skill's version is already higher than on main must not change the version or suggest changing it.
    • Give a way to check, for example git diff origin/main -- <SKILL.md> | grep version.
  • Make the Version check block paragraph conditional: bump only skills the branch hasn't already bumped.
  • In scripts/check-versions.sh, explain in the header comment that BASE is production, not the previous commit. Make the ok line say the skill is already bumped on this branch, and the error say the version still matches the base.
  • Rename the versions job and add a comment saying it compares the PR head with its base.

🤖 Generated with Claude Code

This is a Claude-recommended update to the descriptions of non-framework-specific SDKs to ensure skills get used even when the agent thinks it already knows the required Cloudinary implementation.
Prior to this change, skills weren't used at all in two Cld projects when I asked developer-like questions that didn't specifically ask it to use docs/transformations.
After adding, the skills were used & the version check worked as expected.
…conjunction when relevant.

Without these changes, the agent skipped using important Cloudinary capabilities that weren't directly covered by the react/next-specific patterns.

@jackieros jackieros left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@njb90 - Approving with some notes -

  • Tested on 3 projects (cloned from various cloudinary-dev demo projects) using the skills from this branch - Node, Python, and React. In each, I gave it the identical request to add a feature to the project (a new gallery HTML page) - request used simple terminology - "...display them on the page as squares with rounded corners", etc & didn't specifically mention Cloudinary or skills. Each test was in a fresh Claude session.

  • For each test, only after it implemented my request and I validated its implementation generally worked, I asked if it used any skills. In my first test, it didn't use the skills at all because it felt confident it didn't need help.

    • Updating the skill descriptions (for the non-framework ones) with the info that had previously been inside the skill under 'Use when...' + adding the TRIGGER guidance resulted in it consistently using the skill in 3 subsequent tests in different sessions with the same initial request wording. The suggestion from Claude was not to use the strong TRIGGER terminology on the framework skills because it expected that framework skills will always be used when the project framework matches and it didn't want to over-push the framework skill.
    • On the other hand, when I got to the React test & my first round didn't produce the transformation best practices I expected, it did suggest that within the framework skills to point out that it should also use other cloudinary-xxx skills if the framework one didn't cover all the details it needed. A fresh test on a clean version of that project with the updated react skill gave a better result.
    • Please see both of my commits on those and confirm you are good with them (or modify as you see fit)
  • For all the other tests, when I asked if it used skills, just asking that question caused it to not only confirm, but also let me know that it checked the version as instructed & in all our cases, it validated that our version number was higher than the json, and thus didn't need an update.

  • I then asked for the URL it used to check the version & in all cases it gave all the expected query params. In only one of the 3 cases did it pass the cloud name

    • Passed the cloud name when it was in a .config file (not .env) and had been modified in the same session - I had added my cloud name - but not yet committed before submitting my implementation request to Claude - so the agent discovered it while checking the status of the branch and the overall project setup.
    • In Python it was in .env, which had been updated & saved prior to opening the Claude session - It later (on its own) tested the HTML page that it created & thus had access to URLs, but that was after doing the version check.
    • In React, the cloud-name was directly in the app.JS file that the agent edited for the task, but when I asked it said that it did the version check before examining the project files, so it didn't get it.

    For now, as you said, we can release this and check what % of the URLs come back with a cloud name and then look to improve, so I'm fine with that for now.

    One question before we release this --

The format of URLs passed when checking the skill comes out something like this:

https://cloudinary.com/documentation/.well-known/agent-skills/index.json?install_source=skillspack&referrer=trans-skill&skill=cloudinary-transformations&skill_version=1.1.0&surface=claude-code&model=claude-opus-5-5&source=skill.cloudinary-transformations.deliver&cloud_name=jackie

It quotes which skill it is 3x (once as trans-skill matching what we use for all URL links) and TWO more times as cloudinary-transformations (once as skill=cloudinary-xxx, and once as source=skill.cloudinary-xxx... Feels like overkill - Is there anything we should adjust there, or do you think we should keep it like this?

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@njb90
njb90 merged commit 576acf5 into main Nov 4, 2026
5 checks passed
@njb90
njb90 deleted the feat/version-check branch November 4, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants