anthropics / anthropics/claude-code
Skills UX: local and account stores share one namespace with no reconcile, rename, or staleness signal
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
Feedback from a multi-day exercise auditing and restructuring 24 local skills against 28 claude.ai account skills. Every item below cost real time in that exercise rather than being hypothetical. No customer or proprietary data included.
## 1. Two skill stores share one namespace with no way to tell them apart
There are local skills (`~/.claude/skills//SKILL.md`) and claude.ai account skills (server-side, opaque `skill_01...` ids). The Customize UI lists only the account ones. A local skill never appears there.
In this setup, 28 account skills and 24 local ones coexist, and 9 names exist in both stores with **different content**. Nothing in either interface says which store you are looking at, which copy is newer, or that the other copy exists.
**Impact.** The single most repeated confusion of the whole exercise. It took a deliberate experiment to establish the split, and the mental model only landed after being got wrong several times.
**Ask.** Show the store on every skill. In Customize, indicate that a same-named local skill exists and differs.
## 2. Renaming a skill is not an operation
In the account store the name is the identity, so a rename means create-new plus delete-old. There is no rename affordance.
**Impact.** After merging and renaming skills locally, Customize still shows the old names indefinitely, with no indication they are stale.
**Ask.** A rename that preserves identity and history.
## 3. No reconcile, no diff, no staleness signal
Nothing surfaces "your account copy of this skill is 154 days older than your local one".
**Impact.** Five account skills turned out to be 63 to 154 days stale, found only by diffing by hand. The count was wrong twice before it was right, because exports were scattered across three directories.
**Ask.** A `claude skills diff` / `sync` listing both stores side by side with age and drift.
## 4. The description field is overloaded, with inconsistent limits
`description` is simultaneously the model's trigger surface, the only human-readable text in the UI, and subject to a 1024-character limit in the account store but no limit locally.
**Impact.** Two skills needed their descriptions trimmed purely to satisfy the upload limit. Because the description is also the trigger surface, trimming changes when the skill fires, so each trim had to be verified to confirm every quoted trigger phrase survived (17 of them in one case).
**Ask.** Separate trigger text from display text, or apply the same limit in both stores.
## 5. The packager rejects the local versioning convention
`package_skill.py` refuses a top-level `version:` key. Local skills want one to make drift visible.
**Impact.** Every upload requires staging a modified copy. Editing the live file to comply would remove the version marker that exists to prevent drift in the first place.
**Ask.** Accept and ignore `version:`, or provide a first-class version field.
## 6. Skills only load when triggered, so cross-cutting rules cannot live in them
A rule that must apply to every output cannot live in a skill, because the skill is not loaded unless it triggers.
**Impact.** Learned expensively, three times. A house style rule banning a specific character was mirrored into individual skills and drifted to 149 violations across 10 of 23 files, with nine differently-worded copies of the same rule. A citation rule had nine copies that disagreed on scope. A partially-completed rollout left 19 skills on one version of a shared block and 5 on another. The fix was moving such rules to `CLAUDE.md` plus a PreToolUse hook. That works, but the hook cannot intercept writes made through Bash and cannot police chat prose at all.
**Ask.** A supported way to mark instructions as always-on, or clearer guidance that skills are the wrong home for universal rules.
## 7. Skill edits do not apply to the conversation you are in
A running conversation keeps using the skill content it loaded when the skill first triggered. Only a new conversation re-reads the file.
**Impact.** Easy to believe an edit had no effect, or worse, to verify a fix against stale in-memory content.
**Ask.** Reload on change, or warn that the loaded copy is stale.
## 8. Nothing indicates whether a skill can actually run where it is installed
Of the 24 local skills here, 16 depend on a local SQLite database and local scripts. Uploaded as account skills they would appear normal and fail at run time.
**Impact.** Solved by hand: classify every skill by whether it touches local-only resources, write the verdict into the description as a `LOCAL ONLY` / `ACCOUNT-SAFE` prefix, and add a checker that fails when a skill claims to be portable but has a description too long to upload.
**Ask.** Declare dependencies in frontmatter, and refuse or warn on upload when they cannot be satisfied.
## What had to be built to compensate
- `~/.claude/CLAUDE.md` for the two genuinely universal rules.
- A PreToolUse hook enforcing one of them on Edit/Write/MultiEdit.
- A `check_skills.py` with hard invariants over the whole tree: kebab-case name, frontmatter name matches directory, valid YAML, banned characters, mirrored-rule integrity, referenced CLI flags actually exist, every lifecycle state has exactly one writer, and portability claims consistent with the upload limit.
Most of that is scaffolding for gaps that arguably belong in the product.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reading package_skill.py and check_skills.py, then review the local skill layout under ~/.claude/skills/ and the CLAUDE.md and PreToolUse hook mentioned in the report. The issue combines store identity, rename, reconciliation, limits, portability, loading, and universal rules; scope and completion criteria need to be narrowed to one change before implementation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, sqlite
- Domain
- cli, developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100