umbraco-cms-source
How to read Umbraco CMS source at the release tag the project targets, from a local clone of umbraco/Umbraco-CMS or from GitHub through `gh`. Use when you need to see how the CMS implements something:
SKILL.mdplugins/umbraco-devkit/skills/umbraco-cms-source/SKILL.md
GitHubumbraco-cms-source
The CMS source is the ground truth for how Umbraco behaves. Reading it from a local clone is
much faster than fetching files one at a time from GitHub, and git grep searches a whole
release in one call. Read it at the release the project actually targets: code on main or a
newer major can have APIs, names, or layouts the project's version doesn't.
Find the clone
The clone's path lives in the project's CLAUDE.local.md (personal, gitignored, set up by
project-init), on a line like:
- Umbraco CMS source: /Users/jane/Code/Umbraco-CMS
Use that path as $CMS in the commands below. If it starts with ~, expand it to the full
home-folder path first: inside quotes, ~ isn't expanded and git can't find the folder. If there's no such line, or the path isn't a
git repo, use the GitHub fallback below. Don't clone it yourself from here; project-init offers
that, with the user's yes.
Then find the remote that points at umbraco/Umbraco-CMS. In a direct clone it's origin;
in a clone of someone's fork it's usually upstream. Check once per session:
REMOTE=$(git -C "$CMS" remote -v | awk 'tolower($2) ~ /[\/:]umbraco\/umbraco-cms(\.git)?$/ {print $1; exit}')
If $REMOTE comes back empty, no remote points at Umbraco's repo: use the GitHub fallback below.
Pick the tag
- Get the exact CMS version the project targets: the
Umbraco.Cms.*package version inDirectory.Packages.propsor the.csproj.CLAUDE.md's Stack section has the major if the exact version isn't pinned. - The tag is
release-<version>, e.g.release-17.6.1. For a major-only target, take the newest tag in that major:git -C "$CMS" tag -l "release-17.*" --sort=-v:refname | head -1 - If the tag isn't there, fetch tags once, then look again:
git -C "$CMS" fetch --tags --quiet "$REMOTE" - If the project supports more than one CMS major, check each major's tag. Behavior that differs between them is a multi-version finding, not a detail to average out.
Read, search, list
Read straight from the tag. These commands never touch the clone's working tree or current branch:
# Read one file
git -C "$CMS" show "${tag}:src/Umbraco.Core/Services/ContentService.cs"
# Search the whole release (narrow with a pathspec when you can)
git -C "$CMS" grep -n "class ContentService\b" "${tag}" -- 'src/*.cs'
# List files under a folder
git -C "$CMS" ls-tree -r --name-only "${tag}" -- src/Umbraco.Web.UI.Client/src/packages/core/
Write "${tag}:path" with braces. In zsh, $tag:s is read as a modifier on $tag.
Where to look: from release-17.1.0, the CMS has its own CLAUDE.md at the repo root and in each
project under src/ (src/Umbraco.Core/CLAUDE.md, src/Umbraco.Web.UI.Client/CLAUDE.md, and
so on). Read the one for the area you need, at the same tag, before searching. It maps that
project's folders and names its conventions. Older tags don't have them; list the folder
instead. For a backoffice extension, each feature package's
manifests.ts under src/Umbraco.Web.UI.Client/src/packages/ shows how a built-in extension
of the same type is registered.
Rules
- Never change the clone. No
checkout,switch,pull,reset, or edits. It may be the user's own working copy.fetch --tagsis the only write, and it only adds tags. - Quote the tag you read. When a design, review finding, or decision relies on CMS
source, name the file and tag (
release-17.6.1:src/...) so the claim can be checked. - Source beats memory and docs. If what you remember about a CMS API, or what the docs
(
umbraco-docs) say, differs from the source at the project's tag, the source wins.
No clone: GitHub fallback
Use gh against umbraco/Umbraco-CMS at the same tag:
gh api "repos/umbraco/Umbraco-CMS/contents/src/Umbraco.Core/Services/ContentService.cs?ref=${tag}" \
-H "Accept: application/vnd.github.raw"
gh search code "class ContentService" --repo umbraco/Umbraco-CMS
gh search code only searches the default branch, not a tag. Confirm anything it finds by
reading the file at the project's tag before relying on it.
Without a signed-in gh, read the same file over plain HTTPS at the tag:
https://raw.githubusercontent.com/umbraco/Umbraco-CMS/${tag}/src/Umbraco.Core/Services/ContentService.cs.
There's no search this way, so find the path first (the docs, a stack trace, or the
namespace).