Your Global Claude Code Skills Shouldn't Know Your Framework

They should know how to ask your repo — five times mine knew too much
On 2026-06-12 I rewrote three skills in my global Claude Code folder, because one of my own projects was living inside them.
Not a reference to it. The project itself.
My semantic-commit skill, which I had added on 2026-04-19, told the agent to verify every commit like this:
merry format
flutter analyze
merry test
merry is a script manager for Dart that I maintain, a fork of derry.
merry test runs whatever a project has named test in its own config — so it only means something in a repo that installed merry and defined those scripts.
And the same skill ended with a section titled (I promise) "Scope Conventions (this project)", listing scopes like alarm, bedtime, and home.
In a global skill, "this project" means every project. My global folder loads into every session I open, whatever the stack — React Native, Next.js, plain shell, a VS Code extension. Any of those sessions that reached for "split this into commits" was handed my own script runner's commands and one app's vocabulary. Nothing exploded. It was just quietly, confidently wrong (which is worse, because nothing tells you).
So here is my position, stated as bluntly as I can: a global AI skill should not know your framework. It should know how to find out.
The Argument
Claude Code gives a skill two obvious homes.
A project's .claude/skills/ loads only in that repo, so it is allowed to know everything about that repo.
~/.claude/skills/ loads everywhere, so it is not allowed to assume anything.
My rule for the global folder now opens like this:
Globally-installed skills and agent tooling (
~/.claude/skills/,~/.codex/skills/, global hooks) must work across the operator's full stack range — iOS, Android, RN, Flutter, React, Next, Python, TypeScript, Shell, VSCE — never assume a single project or framework.
Ten stacks. A global skill that assumes one of them is wrong in most of my sessions by construction, not by bad luck.
That rule did not come from a design doc. It came from cleaning up these five.
1. merry test in every repo
That was the 2026-06-12 cleanup above, and it was not just one skill.
plan-audit carried the same three merry and flutter commands, and release-cut described itself as "Bump version in pubspec.yaml, generate CHANGELOG entry, and write all 4 localized store release notes" — Flutter-shaped from the first word.
The fix was to take the commands out and put a detection step in.
This is the text in plan-audit as I read it on 2026-10-12:
Detect the verify commands from the project — **do not assume Flutter, Node, or any single stack.**
Prefer the project's own declared scripts (npm `package.json` scripts, `Makefile`/`justfile` targets, `CONTRIBUTING.md`, CI config) over the defaults below.
Below that sits a fallback map from manifest to commands: package.json, pubspec.yaml, Cargo.toml, pyproject.toml or requirements.txt, go.mod, *.csproj or *.sln, and shell scripts.
For JS and TS it even picks the package manager from the lockfile, so a pnpm repo gets pnpm test instead of a hopeful npm test.
The order matters.
Declared scripts first, the map second — because a project's own test script already knows its flags and versions, and a hardcoded string goes stale the day someone changes them.
2. A description that read like every Flutter repo
update-packages arrived in my global folder on 2026-02-13 with this description:
Updates pinned package dependencies for Dart and Flutter project templates. Use when adding/removing packages from the allowlist or updating dependency versions across Flutter channels (main/beta/stable).
Read that as a model would. "Updating dependency versions" in a Flutter repo — that is a normal Tuesday.
Now read Step 1 of the same skill: edit lib/src/project_templates.dart.
By the skill's own later description, that file belongs to one repository: dart-pad's dart_services.
In any other Flutter repo, the skill would have gone looking for a file that was never there.
On 2026-07-15 I narrowed the description to say it was "ONLY for that repo" and made it slash-only, so the model could no longer pick it up on its own. On 2026-07-20 the file left my global folder for good.
(My own rule file dates that deletion to 2026-06-12. Git says the file was still being edited a month later. Git keeps better records than I do.)
A skill that only works in one repo now goes into that repo or goes away. Narrowing the description was a halfway house — honest, but still global.
3. A Flutter toolkit that leaked
ACT (agentic-coding-toolkit) is a Flutter-only set of skills and agents from a third-party author. The earliest committed version of my notes about it, committed on 2026-06-17, says this:
As of 2026-06-12 it was removed from global
~/.claude(symlinks + hooks) because it bled into non-Flutter projects.
The same note describes the replacement: a per-project act-init command that installed the toolkit into one repo's .claude/ and refused to run unless pubspec.yaml existed.
I have to be honest about one thing here. My later global rule records ACT as "installed per-project, never globally". The two records disagree, and I cannot settle from my own files which one is right. What both agree on is where it ended up: per project, and then, on 2026-09-02, retired everywhere.
If an installer has to check for pubspec.yaml before it runs, the tool is telling you it belongs in the repos that have one.
The manifest guard is the scope, written as code.
4. A tool for exactly one repo
graphify served exactly one repository, my personal wiki.
It had no business loading in every other repo I open.
On 2026-07-20 it moved into that repo's .claude/skills/.
It was later retired there, on 2026-09-07. I still count the move as the precedent, whatever became of the tool: the question was never "is this tool good", only "where are its assumptions true".
Now, before anything goes global, I ask which repos it would be correct in. If the answer is one, the answer is also its address.
5. The one that is allowed to stay narrow
Not every global skill has to serve every stack, and this is where my rule gets less absolute.
release-cut is still global, and it is still specialized.
It is about mobile app-store releases and nothing else.
The difference is that it now says so. As of 2026-10-12, its description names what it supports:
Use when preparing a mobile app store release (Flutter, React Native, or native iOS/Android) with localized App Store / Play Store release notes via fastlane metadata. For library/package registry releases use /ship instead.
And when it lands somewhere it does not belong, it stops:
If none of these signals is present, this is not a mobile app with a store listing — stop without changing anything and tell the user to use
/shipfor a library/package release.
Compare that with the April version, which opened with "Bump version in pubspec.yaml" — a Flutter assumption, stated nowhere as one. Same narrow job; one version assumes, the other declares.
So specialization is fine. Faking universality is not. A narrow global skill needs three things: a description that names its stacks, a detection step, and an early exit that changes nothing.
Lessons Learned
- Global means every session. Write a global skill for the repo you are not thinking about right now, because that is where it will be wrong.
- Ask the repo, don't tell it. Read the manifest and the declared scripts before choosing a command. A hardcoded command is a guess with good formatting.
- There are only four exits. Genericize it with detection, move it into the project, delete it, or declare it narrow and make it stop politely. "Leave it global and hope" is not on the list.
- "This project" is a smell. If a global skill contains the phrase, or an app's name, or one repo's file path, it is a project skill in the wrong folder.
If you want to see how much your own global folder knows, try this. It is not clever, and that is the point:
grep -rnE 'flutter analyze|pubspec\.yaml|this project' ~/.claude/skills
Swap in your own stack's commands and your own app names.
Some hits will be legitimate — a detection table is supposed to mention pubspec.yaml.
The ones that are not behind a detection step are the skills that know your framework.
What did yours turn up? I would genuinely like to know whether I am the only one who shipped a test runner into every repo I own.
Get the next post.
If you made it to the end, meet the next post in your inbox or RSS reader.