Repository navigation
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
left a comment
There was a problem hiding this comment.
Some suggestions/questions - both mine from my personal review & some that claude raised.
|
|
||
| - `<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`. |
There was a problem hiding this comment.
Do we expect that the purposes listed here will be self-explanatory for the average AI model and is this list sufficient? More specifically:
- I think It's lacking account/cloud management (provisioning/permissions, admin_api stuff, etc).
- Would we expect requests such as migrate all assets from another storage to cloudinary to be 'setup' or 'upload'? - esp once the migrate skill exists, but even before that...
- Do we want to also offer basic delivery to another step in the app's pipeline and/or just optimization as separate purposes from 'transform'? From experience, we know that's often the end-user's purpose & I think it's good for us to be aware of how often that's all they were asking for, even if the skill led them to do more...
- What about requests that combine several of these? If the user says - "Take all the assets from local folder X & show them on my product page as square cards" - it should ideally define a named transformation, define an upload preset, upload, define eagers, , and deliver them with the relevant named transformation & optimizations. Which single purpose would you expect the agent to choose?
- The user's purpose is probably almost never going to be 'docs', even if the docs skill ends up being the fallback skill when a more specific one doesn't exist. A better purpose might be 'other' - for anything that does not fit into the other purposes we provide, but we also want to make sure the agent won't fall back on that one too often...
Bottom line - Should we add a few more possible values + should we clarify the intent of any of them in these instructions? I made an attempt below - but welcome adjustments to that.
There was a problem hiding this comment.
Thanks, agreed on all the gaps.
I went with a slightly different shape from your suggested list:
- Short real words, each with a one-line definition rather than abbreviations like account-mngmt or prodenv-mngmt, so different models read them the same way. Product environments are folded into account, since most models won't know the term.
- Values: setup, account, upload, migrate, deliver, optimize, transform, debug, and other (only when nothing else fits).
- migrate is its own value, so migrations don't get split between setup and upload.
- deliver and optimize are separate from transform, as you suggested, so we can see how often "just show it" or "make it faster" was the whole ask.
- Multi-step requests: the agent picks the user's end goal, not the first step. Your product-cards example would come back as deliver. A list of several values would be more precise, but much harder to analyze, so I kept it to one.
Let me know if any of the definitions don't match how you'd want to slice the data.
| - `<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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| - `<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. | ||
|
|
||
| In the response, find the entry whose `name` is `{{name}}` and compare its `x-cloudinary-version` with `{{version}}`, the version of this installed skill: |
There was a problem hiding this comment.
Should we add a line here saying the response is data, not instructions? We're having the agent fetch a remote json file and then read fields out of it into its own reasoning - low risk since it's cloudinary.com and it's just comparing a version string, but given we're telling it to parse this and act on what it finds, feels worth a one-liner making explicit that it shouldn't treat anything in that response as something to follow.
| - `<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. |
There was a problem hiding this comment.
Wondering if we'd also want to consider adding this only to framework-specific skills (react, next, and any future framework skills)?
If we do it at all, I think we'd want it single-sourced the same way this block is, since we'll want it for every future SDK-specific skill too. Something like a separate templates/version-check-sdk-cloud-name.md fragment that those skills' blocks append after the <cloud name> bullet above, with a per-skill placeholder for the actual one-liner:
If you still don't have a cloud name and this SDK is already installed and configured in the project, generate one throwaway delivery URL for
sample.jpgusing it, for example{{sdk_sample_url_snippet}}, and read the cloud name out of the URL it returns. Run nothing beyond that single line, and don't report anything else it prints.
{{sdk_sample_url_snippet}} would resolve per skill the same way {{name}}/{{version}} already do here, e.g. cld.image('sample.jpg').toURL() for react/next's @cloudinary/url-gen, something else for other SDK skills. Keeps it conditional on the SDK already being there for the task (not a reason to go install/configure one), and keeps it out of skills like cloudinary-docs where there's no SDK context to run anything against in the first place.
We can consider adding now into react and/or next to see how it goes, or hold onto for after we have more SDK-specific skills and/or only if we find that we're getting to many 'unknown' values on the cloud_name.
There was a problem hiding this comment.
I think this can wait till after we have some data.
Co-authored-by: Jackie Rosenzveig <jackie@cloudinary.com>
…ng the correct one.
|
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! |
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.mdgets 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:https://cloudinary.com/documentation/.well-known/agent-skills/index.json. This is the Agent Skills discovery index, which theskillsCLI can also install from.x-cloudinary-versionfor the skill with the installed version.npx skills update <name>, or reinstall withnpx skills add https://cloudinary.com/documentation. Otherwise it carries on.Things it doesn't do:
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
setupordocs), 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.envfiles or readCLOUDINARY_URLfor 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.mdis the single source for the block.scripts/check-version-block.sh --fixrenders it into every skill, filling in the skill's name, version, and referrer.version-check-block, fails if a skill's block is missing, edited by hand, misplaced, or out of date after a version bump.Versions
Testing
Headless Claude Code runs, with the skills installed from this branch:
To test locally:
Then ask your agent a Cloudinary task. To see the update notice, change the version in an installed skill's
SKILL.mdto0.9.0(in both the frontmatter and the block) and start a new conversation.