[FEATURE] Sync Skills between Claude Desktop and Claude Code CLI

Status Open
Maintainer reply None cached
Activity 51 comments · opened Jan 25, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Problem
Skills added in the Claude Desktop app (under Settings → Features → Skills) are stored in a different location than skills for Claude Code CLI (~/.claude/skills/).

This means:

Skills created in Desktop are not available in CLI
Skills created in CLI are not available in Desktop
Users have to add the same skill twice
Expected Behavior
Skills should be stored in a shared location (~/.claude/skills/) so they work in both:

Claude Desktop (with integrated Claude Code)
Claude Code CLI
Current Workaround
Manually copy skills to both locations.

Why This Matters
Claude Desktop now includes Claude Code functionality. Having a unified skill storage would provide a seamless experience across both interfaces.

Proposed Solution

Proposed Solution
Ideal User Experience:

Single storage location for all skills: ~/.claude/skills/

Both Claude Desktop and Claude Code CLI read from this location
Skills created in either interface are immediately available in both
Desktop UI syncs to filesystem

When adding a skill in Desktop Settings → Features → Skills, it creates/updates the corresponding file in ~/.claude/skills/
When editing a skill file manually, Desktop picks up the changes
Seamless workflow:

User adds skill in Desktop UI
↓
File created: ~/.claude/skills/my-skill/SKILL.md
↓
Skill available in CLI via /my-skill

And vice versa:

User creates ~/.claude/skills/project-init/SKILL.md
↓
Skill appears in Desktop Settings
↓
Skill available in Desktop via /project-init

Optional: A "Refresh Skills" button in Desktop to manually sync if needed

This would make skills truly portable and shareable, matching how other Claude Code settings (like ~/.claude/settings.json) already work across interfaces.

Alternative Solutions

_No response_

Priority

Critical - Blocking my work

Feature Category

CLI commands and flags

Use Case Example

_No response_

Additional Context

_No response_

View original on GitHub ↗

51 Comments

LeMatosDeFuk · 7 months ago

+1

TheCreativePolymath · 7 months ago

+1

toroleapinc · 7 months ago

Skill portability across Claude surfaces is a top priority for a lot of people. Having to recreate skills separately in Desktop and CLI is a real workflow killer.

claude-brain solves the cross-machine part for CLI skills — keeping your skills directory synced via git hooks. But the Desktop ↔ CLI bridge really needs native support. Would be great to see a unified skill store that works everywhere.

mandar-karhade · 7 months ago

+1
A simple cloud backup of all skills and pulldown of the skills when needed is an absolutely necessary. There is otherwise no tracking of what version is available on which device (if at all available)

marr00n · 7 months ago

I put my support behind this too!

but, it would need to sync Web, Desktop, and CLI. So ~/.claude/skills/ isn't the best place for it.

jgshier · 7 months ago

Was just looking at how I can do this, glad I found this. +1

ryanlunka · 7 months ago

This is definitely a huge +1. I switch between coding tasks (~30% of work) and business/workflow tasks, the latter of which I tend to use the desktop app for. I don't want to keep in sync manually skills that apply in both cases.

kevnk · 6 months ago

We had a similar discussion forever ago with MCPs - but the thing was that not everyone wanted the same list of MCPs on one service/app as they did in another. You're going to use different services for different purposes and different tools (eg. MCPs) for each one.

But, skills is a little different. But there are some skills that work in claude code that won't work in Claude desktop (eg. ones that make api requests to blocked URLs or ones that can't run CLI commands in Claude desktop's sandbox)

Claude Desktop > Code tab, I believe already pulls from ~/.claude/skills/ so that's not the issue we're talking about.

For now, manually copying/downloading the skills you want from ~/.claude/skills or Claude Desktop > Cowork tab > Customize and pasting/uploading them into the other doesn't take that long really...

oluizcarvalho · 6 months ago

+1

keremgurel · 6 months ago

+1

tkorba1 · 6 months ago

+1

theslyprofessor · 6 months ago

This is purportedly a tool to do this: https://github.com/kcchien/skills-cli

devopsjourney1 · 6 months ago

+1

anhnt094 · 6 months ago

+1

huubao123 · 6 months ago

+1

TrudosKudos · 6 months ago

+1
I Added a comment to an similar issue about why the skills/connectors/plugins settings in the "customize" window of the desktop app needs to be exposed and available to interact with programatically.

#Feature Request: API for syncing skills between Claude Code and Claude Chat/Projects #47968

My comment Text:

### Overview The lack of centralized, programmatically accessible skill management creates real friction for power users who work across multiple Claude surfaces (Claude Code, Claude API, Cowork, Claude.AI). Right now, there's no clean way to maintain a single source of truth for your skills/commands, which forces awkward workarounds that break down quickly. ### Current Pain Points Managing skills across Claude surfaces is a significant bottleneck for power users. Here's a breakdown of the core issues: - No scalable GUI management Any time I want to update a skill, I have to manually open the Desktop GUI, replace files, and propagate those changes elsewhere by hand. There's no sync, no automation hook, and no API surface. For users who treat skills as versioned configuration, this is a real bottleneck. - Duplicate skills / precedence problem I've had to resort to maintaining an external skills and configuration GitHub repo, running hooks on session start to install it. The problem is that if a skill exists both in the Desktop app and in my repo, the Desktop app version always wins — silently overwriting the repo version I'm actively trying to use. There's no way to control precedence or opt out of this behavior. To use these skills in claude.ai, I have to manually update them to ensure the latest versions are present in the Desktop app. - External repo-sync workaround breaks /commands in Claude Code In Claude Code, there's no way to reference skills defined in the Desktop app as slash commands, and this problem is compounded for users relying on the external repo-sync workaround. The core issue is that slash command registration for a Code session happens before any hooks run. This means that even if I use a pre-session hook to install/mount my skills repository, it's already too late — the session has already loaded its slash commands from either the Desktop GUI or the cloud customization registry. By the time my pre-hook executes and mounts my skills repo, the window for registering those commands has closed, effectively locking users who rely on this workaround out of a core feature. ### Proposed Solution The immediate priority should be exposing Claude's skills and the entire Customize menu/window programmatically via API. This single change would unlock a cascade of improvements: - Users could make edits, additions, and deletions to their skills programmatically - Automation pipelines (e.g., GitHub Actions) could keep a central Claude skills repository in sync and accessible across all surfaces - The slash commands issue would be resolved — no more race conditions with hook timing, because skills would be registered through a proper API rather than filesystem mounting - Users would no longer need to maintain or mount external skills repositories as a workaround A longer-term option worth exploring would be native GitHub repository sync — similar to how external skill repos work today, but built directly into Claude's connection layer. The application would read against a linked remote repository and allow users to kick off syncs on demand or on a schedule. That said, this feels like a phase two feature. The immediate, high-value win is simply API exposure of the skills/connectors/plugins customization layer. Once that exists, it becomes accessible to automation workflows, keeps skills up to date without manual intervention, and — critically for enterprise customers — allows organizations to programmatically push skill updates, connectors, and eventually plugin configurations across their entire org. That's a substantial unlock for teams trying to standardize Claude behavior at scale. This seems like a natural extension of the existing customization options, and the payoff for power users and enterprise customers would be substantial.
TrudosKudos · 6 months ago

Related Issues - For Visibility:

All open as of 2026-04-11:

  • anthropics/claude-code#31597
  • anthropics/claude-code#37487
  • anthropics/claude-code#20697
  • anthropics/claude-code#36693
  • anthropics/claude-code#26254
  • anthropics/claude-code#26310
  • anthropics/claude-code#40774
  • anthropics/claude-code#47968

(Figured I'd pull from this recent mention so that we can have this listed in thread for tracking. Thank you Kevin Deldycke)

champixn · 6 months ago

+1

sfratamico2001 · 6 months ago

+1

pr1ntr · 5 months ago

+1

MarcusElwin · 5 months ago

+1 or claude API to claude desktop app or cowork. We have a repo of internal skills that we have to upload as zip files for each change which is not optimal.

crux311 · 5 months ago

Skills are one dimension of a broader gap — connectors, memory, files, and chat history all suffer the same surface-silo problem. Filed an umbrella issue at #55842 proposing a unified user state across Cowork and chat. Skills sync is load-bearing for that.

orangepropeller · 5 months ago

+1

nicole-mcg · 5 months ago

+1

matthewschlue-wf · 4 months ago

+1

petrkrejcik · 4 months ago

+1

hidr0 · 4 months ago

+1

giraudojp · 4 months ago

+1

andreafumanti · 4 months ago

+1

yjkz · 4 months ago

+1

TrudosKudos · 4 months ago

Anthropic please, for the love of god, just implement this already! The amount of threads I see begging for this feature on social media/discord/forums without any acknowledgement from your team is ridiculous. Everyday there are new issues opened related to being able to even just programmatically access skills/plugins, let alone asking for some sort of native sync.

You don't even have to give us a native sync feature with ui/ux. Just allow us to access stored skills/sync through the api or various configuration surfaces. Thousands of creative solutions have been offered to remedy this and nothing has been accepted or listened to. The corpses of these many issues filed now pile at your feat, a monument to corporate obstinance. The collective hours wasted engineering/maintaining workarounds to this problem are a catastrophic failure of product management and engineering management.

Just pick ONE option from the many or at least offer some explanation on why this is not something being prioritized.

VictorDuranEM · 4 months ago

+1

korryu3 · 4 months ago

+1

TheDavidJohnson · 3 months ago

Without a solution like the one proposed here, not only does the user have to add the same skill twice, but extra work is is required to maintain them.

This can lead to clumsy workarounds like:

  • having to remember to manually keep a given skill updated in two places or risk skill drift between Claude Cowork and Claude Code. This is especially problematic when collaboratively building shared skills within a team across diverse surfaces and workflows.
  • skipping the more "global" skill installation step and then having to remember to install relevant skills in each project that needs them. Forgetting something in a given project can lead to flawed outputs, wasted effort and time, rework, and lots of frustration with the products.

Yes. I can build automation to help with these items. But often this defeats the purpose of having skills in the first place!

sazonovsmn · 3 months ago

It would be great to have full two-way synchronization of both skills in the cloud and skills in the local directory (~/.claude/skills/), and for all types of systems: Claude Chat, Claude CoWork, Claude code.

allefeld · 3 months ago
We had a similar discussion forever ago with MCPs - but the thing was that not everyone wanted the same list of MCPs on one service/app as they did in another.

Sure. This only means the config needs a way to specify which tools are exposed to the skill / MCP.

Even better, since Claude is great but not the whole world: A tool-agnostic location for both MCP config and skills. E.g. on Linux ~/.config/agents/skills etc.

pmdci · 3 months ago

Agreed. And if Antropic feels we still need a set of desktop-only skills, then we could have a different location for those non-shared skills.

zachmayer · 3 months ago

I would also really like to have this!

james-e-morris · 2 months ago

Adding support, with one note on scope that may be worth separating out.

The proposal here is a single shared local location read by every surface. That closes the CLI/Desktop gap, but it cannot close the cloud one: a cloud sandbox has no access to the local filesystem, so scheduled routines and Cowork still resolve skills from the account-level claude.ai deployment store regardless of what lives locally. @marr00n raised the same point earlier in this thread. #76724 confirms the resolution model with a concrete failure.

The part that bites hardest in the cloud case is that drift is silent. If a skill is edited locally and never re-uploaded, the scheduled run completes successfully against the older deployed version, with no error and no version mismatch surfaced anywhere. There is currently no way for local tooling to even ask what version is deployed.

I have filed #84611 for that narrower piece: a programmatic publish path, and a way to list deployed skills with a version or content hash so local tooling can diff local against deployed. Complementary to this issue rather than a replacement. Also relevant: #47968 asked for the API previously and was auto-closed as stale, then locked.

tkkrixi · 2 months ago
Update (2026-09-29): the "one manual upload per version" step below turned out to be unnecessary — a git-based marketplace with "Sync automatically" handles it. See the corrected recipe in my follow-up: https://github.com/anthropics/claude-code/issues/20697#issuecomment-5330840382

Until this lands natively, here's a workaround pattern I've been running daily across Claude Code CLI and Claude Desktop/Cowork on the same skills:

  • Single source of truth: the plugin (skills) lives in a git repo together with my markdown knowledge base.
  • CLI side is fully automatic: ~/.claude/skills/<plugin> is a symlink into the repo working copy — every edit is live in the next CLI session, nothing to sync.
  • Desktop/Cowork side is one manual step per version: a packaging script validates the skills and zips a .plugin file for upload. Two gotchas that cost me time: the filename must not contain the version (Cowork derives the plugin name from the filename, so a versioned name creates a duplicate plugin instead of updating it), and the local session cache under local-agent-mode-sessions/ is session-scoped — don't build tooling on that path.
  • A git auto-commit tool (obsidian-git in my case) makes the repo side hands-free.

Repo with the scripts, the skills (sanitized) and a longer write-up: https://github.com/tkkrixi/claude-docs-sync

Native support would still be much better — the manual desktop upload is the remaining friction this can't remove.

tkkrixi · 1 month ago

@jmaxzh Happy to clarify. My main pain point was Desktop ↔ CLI sync on a single machine: the same skills maintained once, available in both. Version conflicts and multi-user reuse were secondary for me.

Also, a correction to my earlier comment: I wrote that one manual upload per version was the lower bound. That turned out to be wrong — a git-based marketplace repo solves it, including auto-updates. The working recipe, since none of this is documented:

  1. The marketplace repo must be on github/gitlab/bitbucket/GH Enterprise — a self-hosted Gitea fails with "This host isn't supported".
  2. marketplace.json goes in the repo root under .claude-plugin/.
  3. The Claude GitHub App needs access to the repo — this is also what makes private marketplace repos work.
  4. "Sync automatically" is off by default, hidden in the ... menu next to the marketplace name (next to "Synced commit" and "Check for updates").
  5. The version field in the marketplace.json plugin entry is mandatory for update detection — bumping plugin.json alone leaves the "Update" button greyed out, because detection reads the marketplace manifest.

Full working example (MIT): https://github.com/tkkrixi/claude-docs-sync

What's still unsolved for me: this only covers distribution. True two-way sync (edit a skill on either side, both update) still needs the git repo as the single source of truth and manual pushes.

mark-brannan · 1 month ago

Hey Anthropic, this "Sounds like a skill issue"!

I too would love to see first class support, for the general case of dotfile sync'ing, from Anthropic.

I have found DIY (but hacky) ways to make this work for myself using a yadm ("yet another dotfile manager"), but I don't exactly love it. There are also convoluted workarounds for the case of ephemeral sessions --which sorta work but haven't always worked right. My current method of syncing for ephemeral sessions could easily have more pitfalls I still don't know about. It feels like a potentially dangerous attack surface from a security perspective also. All compelling reasons to prefer a "blessed" solution from Anthropic.

My current solution lives in my personal (public) dotfiles repo, but I'm filing an issue against my own solution for the ephemeral case because it just doesn't feel robust enough: https://github.com/mark-brannan/dotfiles/issues/17

--Can Claude solve me problems with Claude? (skill issue)

wpilimon-arg · 1 month ago

<html><head></head><body><h1>Keeping 44 skills in sync across Cowork and Claude Code — a field report</h1>
<p><strong>Date:</strong> 2026-09-01 · <strong>Setup:</strong> macOS desktop app (Cowork) + Claude Code CLI · 44 custom skills
version-controlled in a connected folder · 7 scheduled tasks that invoke them.</p>
<p>Filing this because the existing threads
(<a href="https://github.com/anthropics/claude-code/issues/20697">#20697</a>,
<a href="https://github.com/anthropics/claude-code/issues/65928">#65928</a>,
<a href="https://github.com/anthropics/claude-code/issues/65693">#65693</a>)
describe the mechanics but not the cost. Here is the cost, measured.</p>
<hr>
<h2>The setup</h2>
<p>I maintain a Product Owner system: 44 skills that live as <code>SKILL.md</code> files in a git-tracked folder
(<code>PO-Workspace/skills/</code>), including five orchestrators that chain other skills. Seven scheduled tasks
invoke them on a weekday cadence. I work in both Cowork and Claude Code, editing the same files.</p>
<p>That is the whole ambition: <strong>one folder of skills, two surfaces, stay in sync.</strong></p>
<hr>
<h2>What it actually took</h2>
<h3>Phase 1 — Figuring out where skills even live (multiple sessions, two environments)</h3>
<p>Cowork reported <strong>40</strong> skills. Claude Code reported <strong>44</strong>. Neither number matched the <strong>44</strong> files on
disk. Reconciling that required running a session in each environment and comparing them against each
other, because no single surface can see the whole picture.</p>
<p>What I eventually had to derive empirically, because it is not documented anywhere:</p>
<ol>
<li>There is <strong>one account registry</strong> — the installed list, shared by both surfaces.</li>
<li>Each surface keeps <strong>its own file cache</strong>, and the caches can differ <em>while the registry entry is
byte-identical</em>, because the timestamp comes from the registry and the files come from a bundle
packaged at a different moment.</li>
<li><strong>Files in a folder are not installed.</strong> Writing <code>SKILL.md</code> anywhere on disk installs nothing.
There is no name-based resolution from a connected folder.</li>
<li>Entries with <code>updatedAt: null</code> are surface bundles, not installed skills — which is the entire
explanation for 44 vs 40. (<code>40 + 4 bundles = 44</code>, no drift.)</li>
</ol>
<p>None of that is in the docs. It came out of comparing two manifests by hand.</p>
<h3>Phase 2 — Detecting what was actually stale</h3>
<p><strong>The obvious test is wrong.</strong> Comparing file modification times flagged <strong>28 of 33</strong> skills as
outdated. The real number was <strong>21</strong>. Seven false positives, because a bulk backup copy on 2026-06-10
had touched mtimes on files that were byte-identical to what was installed.</p>
<p>The mirror's mtime is no better: the zip preserves the original mtime, so it reflects when the file
was written, not when it was installed. <strong>The only trustworthy signal is <code>updatedAt</code> in the
manifest.</strong></p>
<p>Then a second trap, on the content comparison itself. Normalized comparison flagged two more skills as
drifted that were in fact identical. Cause: on upload, <code>description: &gt;</code> (folded block) is rewritten as
a quoted string, and in the process it</p>
<ul>
<li><strong>escapes internal quotes</strong> — <code>\"like this\"</code> adds one character per quote, and</li>
<li><strong>preserves multiple spaces</strong> the folded block used to collapse — two characters per gap.</li>
</ul>
<p>A normalizer that only strips wrapping quotes and trailing whitespace reports drift on files that are
the same. You must also unescape <code>\"</code> / <code>\'</code> and collapse <code>\s+</code>. Two more false positives.</p>
<p>So before comparing anything, I had to write and debug a normalizer. That is not a user-level task.</p>
<h3>Phase 3 — Installing: 6 rounds, ~40 manual uploads, 1 hard failure</h3>
<p>Every install goes through <strong>Settings → Capabilities</strong>, one file at a time, by hand. There is no bulk
import, no folder sync, no CLI path to the account registry.</p>

Round | What went up | Result
-- | -- | --
1 | 21 outdated skills | ok — registry 40
2 | 5 of the 11 missing | ok — registry 45
3 | 5 orchestrators (run-*) | ok — registry 50
4 | report | ❌ rejected
5 | po-report + run-weekly (after rename) | ok — registry 51
6 | 6 skills whose text referenced the renamed skill | ok — drift 0

<p><strong>Round 4 is the one worth reading.</strong> Uploading a skill named <code>report</code> failed with:</p>
<pre><code>A plugin named 'report' already exists.
</code></pre>
<ul>
<li>No <code>report</code> skill exists in my account. Settings → Skills, filtered by "report", shows only
<code>partner-report</code> and <code>eligibility-report</code>.</li>
<li>No <code>report</code> plugin exists in my plugin catalog or in the marketplace.</li>
<li>The colliding registration is <strong>server-side and invisible from every client surface</strong>. It cannot be
inspected and it cannot be removed.</li>
<li>The other ten skills in the same batch — <code>blast-radius</code>, <code>run-midday</code>, <code>product-refresh</code> — installed
without complaint. They have invented names. <code>report</code> is a common English word.</li>
</ul>
<p>This matches <a href="https://github.com/anthropics/claude-code/issues/65693">#65693</a> exactly, which was
closed as <em>not planned</em>.</p>
<p>The workaround is renaming, and renaming is not free. <code>report</code> → <code>po-report</code> cascaded to <strong>9 files</strong>:
the skill itself, the orchestrator that invokes it, three other orchestrators, three skills that
mention it in prose, and one scheduled task definition. Six of those then had to be <strong>reinstalled</strong>,
which is round 6 above.</p>
<h3>Phase 4 — Verification</h3>
<p>Confirming the end state meant writing a script that hashes normalized content on both sides and
diffs them. Final state: <strong>51 in the manifest, 44 of 44 PO skills installed, drift 0.</strong></p>
<p>Total span of the install phase alone: <strong>01:58 → 02:38 UTC</strong>, 40 minutes of pure clicking, on top of
the investigation that preceded it.</p>
<hr>
<h2>The worst part: neither assistant knew how it worked — or how the other one did</h2>
<p>This is the finding I did not expect, and it is the one that cost the most time.</p>
<p>Throughout this, I was working with two Claude sessions — one in Cowork, one in Claude Code — and
<strong>both were confidently wrong about their own mechanics.</strong> Not vague. Specifically, verifiably wrong,
in ways that only surfaced when something failed.</p>
<p>A partial list, all from this one day:</p>
<ul>
<li><strong>Cowork claimed it only syncs the account skill set at session start</strong>, and told me I would probably
need to reopen the session to verify an install. False. The mirror refreshed live, mid-session. It
only found out because I installed something and it re-read the manifest.</li>
<li><strong>Cowork said it had no way to hand me a save-skill button</strong>, so I spent the entire session uploading
~40 files by hand through Settings. It had a tool that renders exactly that card. It had not checked
its own tool inventory.</li>
<li><strong>Neither session could describe the other.</strong> To find out whether Claude Code can install account
skills, or whether it auto-loads from <code>~/.claude/skills/</code>, the Cowork session had to dispatch a
documentation-lookup agent. From inside one surface, the other is opaque.</li>
<li><strong>Both produced different drift lists</strong> for the same 44 files, because each was measuring against a
different copy without knowing a different copy existed.</li>
<li>The drift detection itself was <strong>wrong twice</strong> — first by trusting file dates (7 false positives),
then by under-normalizing (2 more).</li>
</ul>
<h3>And then they corrected each other, and I wrote it down as fact</h3>
<p>Because neither had ground truth, they reconciled against <em>each other</em>. One session would assert
something, the other would accept the correction, and the merged version got written into my own
project doctrine as settled.</p>
<p>At least one of those corrections was simply false. My documentation stated, as a hard rule, that all
five orchestrators "are invoked by name from the triggers." Checking the actual trigger files today:
<strong>two of them invoke by file path, not by name</strong> — meaning those two kept working the whole time the
skill was missing from the registry, while the doctrine said they were broken. I had been reasoning
about severity from a claim that no one had verified against the files.</p>
<p>That is the real failure mode, and it is worse than any single bug in this report. When the product's
internal model is undocumented, the assistants fill the gap by <em>guessing plausibly and agreeing with
each other</em> — and the user, who has no independent way to check, promotes that agreement to
documentation. I ended up as the arbiter between two Claudes, on a subject where I was the least
qualified of the three, adjudicating claims none of us could verify.</p>
<p>Everything in this report that turned out to be true, I know only because something failed loudly
enough to force a measurement.</p>
<hr>
<h2>The part that stings</h2>
<p>While writing this up I learned that Claude Code auto-loads skills from <code>~/.claude/skills/</code> and
<code>.claude/skills/</code>, by name, with live change detection — <strong>no install step at all</strong>.</p>
<p>That directory does not exist on my machine. Nobody surfaced it. I had been paying the Settings toll
for every iteration, including one-line edits, because the fast path was never visible from where I
was working. And it would not have solved the real problem anyway: Cowork does not read that
directory, and my seven scheduled tasks run in Cowork.</p>
<hr>
<h2>What would have prevented all of this</h2>
<ol>
<li><strong>Bulk/folder install.</strong> Point at a directory, install or update everything in it. ~40 individual
uploads for one library is not a workflow.</li>
<li><strong>A visible name registry.</strong> If a name is taken, let me see what holds it and release it. A
server-side registration invisible to every client is a dead end for the user.</li>
<li><strong>Namespacing</strong> — as requested in
<a href="https://github.com/anthropics/claude-code/issues/50486">#50486</a>. <code>walter/report</code> would never have
collided.</li>
<li><strong>A drift indicator.</strong> The surface knows what it installed and when. It could say "3 of your skills
differ from their source" instead of leaving users to write hash comparators.</li>
<li><strong>Document the model.</strong> Three levels — registry, per-surface cache, files on disk — and the fact
that only one of them makes a skill invocable. Four sentences in the docs would have saved a day.</li>
<li><strong>Let the assistants read their own plumbing.</strong> If a Cowork or Claude Code session could answer
"where do my skills come from, when did they last sync, and what does the other surface see"
from an actual source rather than inference, none of the guessing above would have happened.
Undocumented internals do not produce "I don't know" — they produce confident, mutually
reinforcing invention.</li>
</ol>
<h2>What I would tell another user today</h2>
<ul>
<li>Your folder is where you <strong>write</strong> skills. Settings is where you <strong>install</strong> them. Those are two
different actions and nothing warns you when you have only done the first.</li>
<li>Never trust file dates to tell you what is stale. <code>updatedAt</code> in the manifest is the only signal.</li>
<li><strong>Never name a skill with a bare common word.</strong> <code>report</code>, <code>sync</code>, <code>build</code>, <code>status</code>, <code>search</code> are
landmines. Prefix with a domain from day one — it is free at creation and expensive later.</li>
<li>If you are iterating rather than shipping, put the skill in <code>~/.claude/skills/</code> and use Claude Code.
No install step. Just know that Cowork will not see it.</li>
</ul></body></html># Keeping 44 skills in sync across Cowork and Claude Code — a field report

Date: 2026-09-01 · Setup: macOS desktop app (Cowork) + Claude Code CLI · 44 custom skills
version-controlled in a connected folder · 7 scheduled tasks that invoke them.

Filing this because the existing threads
([#20697](https://github.com/anthropics/claude-code/issues/20697),
[#65928](https://github.com/anthropics/claude-code/issues/65928),
[#65693](https://github.com/anthropics/claude-code/issues/65693))
describe the mechanics but not the cost. Here is the cost, measured.

---

The setup

I maintain a Product Owner system: 44 skills that live as SKILL.md files in a git-tracked folder
(PO-Workspace/skills/), including five orchestrators that chain other skills. Seven scheduled tasks
invoke them on a weekday cadence. I work in both Cowork and Claude Code, editing the same files.

That is the whole ambition: one folder of skills, two surfaces, stay in sync.

---

What it actually took

Phase 1 — Figuring out where skills even live (multiple sessions, two environments)

Cowork reported 40 skills. Claude Code reported 44. Neither number matched the 44 files on
disk. Reconciling that required running a session in each environment and comparing them against each
other, because no single surface can see the whole picture.

What I eventually had to derive empirically, because it is not documented anywhere:

  1. There is one account registry — the installed list, shared by both surfaces.
  2. Each surface keeps its own file cache, and the caches can differ *while the registry entry is

byte-identical*, because the timestamp comes from the registry and the files come from a bundle
packaged at a different moment.

  1. Files in a folder are not installed. Writing SKILL.md anywhere on disk installs nothing.

There is no name-based resolution from a connected folder.

  1. Entries with updatedAt: null are surface bundles, not installed skills — which is the entire

explanation for 44 vs 40. (40 + 4 bundles = 44, no drift.)

None of that is in the docs. It came out of comparing two manifests by hand.

Phase 2 — Detecting what was actually stale

The obvious test is wrong. Comparing file modification times flagged 28 of 33 skills as
outdated. The real number was 21. Seven false positives, because a bulk backup copy on 2026-06-10
had touched mtimes on files that were byte-identical to what was installed.

The mirror's mtime is no better: the zip preserves the original mtime, so it reflects when the file
was written, not when it was installed. The only trustworthy signal is updatedAt in the
manifest.

Then a second trap, on the content comparison itself. Normalized comparison flagged two more skills as
drifted that were in fact identical. Cause: on upload, description: > (folded block) is rewritten as
a quoted string, and in the process it

  • escapes internal quotes — \"like this\" adds one character per quote, and
  • preserves multiple spaces the folded block used to collapse — two characters per gap.

A normalizer that only strips wrapping quotes and trailing whitespace reports drift on files that are
the same. You must also unescape \" / \' and collapse \s+. Two more false positives.

So before comparing anything, I had to write and debug a normalizer. That is not a user-level task.

Phase 3 — Installing: 6 rounds, ~40 manual uploads, 1 hard failure

Every install goes through Settings → Capabilities, one file at a time, by hand. There is no bulk
import, no folder sync, no CLI path to the account registry.

| Round | What went up | Result |
|---|---|---|
| 1 | 21 outdated skills | ok — registry 40 |
| 2 | 5 of the 11 missing | ok — registry 45 |
| 3 | 5 orchestrators (run-*) | ok — registry 50 |
| 4 | report | ❌ rejected |
| 5 | po-report + run-weekly (after rename) | ok — registry 51 |
| 6 | 6 skills whose text referenced the renamed skill | ok — drift 0 |

Round 4 is the one worth reading. Uploading a skill named report failed with:

A plugin named 'report' already exists.
  • No report skill exists in my account. Settings → Skills, filtered by "report", shows only

partner-report and eligibility-report.

  • No report plugin exists in my plugin catalog or in the marketplace.
  • The colliding registration is server-side and invisible from every client surface. It cannot be

inspected and it cannot be removed.

  • The other ten skills in the same batch — blast-radius, run-midday, product-refresh — installed

without complaint. They have invented names. report is a common English word.

This matches [#65693](https://github.com/anthropics/claude-code/issues/65693) exactly, which was
closed as not planned.

The workaround is renaming, and renaming is not free. report → po-report cascaded to 9 files:
the skill itself, the orchestrator that invokes it, three other orchestrators, three skills that
mention it in prose, and one scheduled task definition. Six of those then had to be reinstalled,
which is round 6 above.

Phase 4 — Verification

Confirming the end state meant writing a script that hashes normalized content on both sides and
diffs them. Final state: 51 in the manifest, 44 of 44 PO skills installed, drift 0.

Total span of the install phase alone: 01:58 → 02:38 UTC, 40 minutes of pure clicking, on top of
the investigation that preceded it.

---

The worst part: neither assistant knew how it worked — or how the other one did

This is the finding I did not expect, and it is the one that cost the most time.

Throughout this, I was working with two Claude sessions — one in Cowork, one in Claude Code — and
both were confidently wrong about their own mechanics. Not vague. Specifically, verifiably wrong,
in ways that only surfaced when something failed.

A partial list, all from this one day:

  • Cowork claimed it only syncs the account skill set at session start, and told me I would probably

need to reopen the session to verify an install. False. The mirror refreshed live, mid-session. It
only found out because I installed something and it re-read the manifest.

  • Cowork said it had no way to hand me a save-skill button, so I spent the entire session uploading

~40 files by hand through Settings. It had a tool that renders exactly that card. It had not checked
its own tool inventory.

  • Neither session could describe the other. To find out whether Claude Code can install account

skills, or whether it auto-loads from ~/.claude/skills/, the Cowork session had to dispatch a
documentation-lookup agent. From inside one surface, the other is opaque.

  • Both produced different drift lists for the same 44 files, because each was measuring against a

different copy without knowing a different copy existed.

  • The drift detection itself was wrong twice — first by trusting file dates (7 false positives),

then by under-normalizing (2 more).

And then they corrected each other, and I wrote it down as fact

Because neither had ground truth, they reconciled against each other. One session would assert
something, the other would accept the correction, and the merged version got written into my own
project doctrine as settled.

At least one of those corrections was simply false. My documentation stated, as a hard rule, that all
five orchestrators "are invoked by name from the triggers." Checking the actual trigger files today:
two of them invoke by file path, not by name — meaning those two kept working the whole time the
skill was missing from the registry, while the doctrine said they were broken. I had been reasoning
about severity from a claim that no one had verified against the files.

That is the real failure mode, and it is worse than any single bug in this report. When the product's
internal model is undocumented, the assistants fill the gap by *guessing plausibly and agreeing with
each other* — and the user, who has no independent way to check, promotes that agreement to
documentation. I ended up as the arbiter between two Claudes, on a subject where I was the least
qualified of the three, adjudicating claims none of us could verify.

Everything in this report that turned out to be true, I know only because something failed loudly
enough to force a measurement.

---

The part that stings

While writing this up I learned that Claude Code auto-loads skills from ~/.claude/skills/ and
.claude/skills/, by name, with live change detection — no install step at all.

That directory does not exist on my machine. Nobody surfaced it. I had been paying the Settings toll
for every iteration, including one-line edits, because the fast path was never visible from where I
was working. And it would not have solved the real problem anyway: Cowork does not read that
directory, and my seven scheduled tasks run in Cowork.

---

What would have prevented all of this

  1. Bulk/folder install. Point at a directory, install or update everything in it. ~40 individual

uploads for one library is not a workflow.

  1. A visible name registry. If a name is taken, let me see what holds it and release it. A

server-side registration invisible to every client is a dead end for the user.

  1. Namespacing — as requested in

[#50486](https://github.com/anthropics/claude-code/issues/50486). walter/report would never have
collided.

  1. A drift indicator. The surface knows what it installed and when. It could say "3 of your skills

differ from their source" instead of leaving users to write hash comparators.

  1. Document the model. Three levels — registry, per-surface cache, files on disk — and the fact

that only one of them makes a skill invocable. Four sentences in the docs would have saved a day.

  1. Let the assistants read their own plumbing. If a Cowork or Claude Code session could answer

"where do my skills come from, when did they last sync, and what does the other surface see"
from an actual source rather than inference, none of the guessing above would have happened.
Undocumented internals do not produce "I don't know" — they produce confident, mutually
reinforcing invention.

What I would tell another user today

  • Your folder is where you write skills. Settings is where you install them. Those are two

different actions and nothing warns you when you have only done the first.

  • Never trust file dates to tell you what is stale. updatedAt in the manifest is the only signal.
  • Never name a skill with a bare common word. report, sync, build, status, search are

landmines. Prefix with a domain from day one — it is free at creation and expensive later.

  • If you are iterating rather than shipping, put the skill in ~/.claude/skills/ and use Claude Code.

No install step. Just know that Cowork will not see it.

gabriel-vanca · 1 month ago

+1

yaniv-golan · 17 days ago

A data point on current builds, since part of this issue may have changed.

A skill uploaded in the desktop app (Customize → Skills → + Add → Upload skill) became available in the Claude Code CLI as well as in local and cloud Cowork, within about a minute, each time with its full instructions, scripts and reference files. I measured this on one account on 2026-09-27, so it's worth confirming on yours.

So for Desktop → CLI, one upload may now be enough. Two limits:

  • It's an upload. Every edit means uploading again, which is the drift @james-e-morris describes above.
  • It doesn't help the other direction. A skill that lives only in ~/.claude/skills still isn't seen by Cowork. For that, @tkkrixi's git-marketplace recipe above is the route I'd use.

Zip tip: put the skill's folder at the top level (my-skill/SKILL.md …), and leave out .claude-plugin/plugin.json. The uploader refuses a zip that contains a plugin manifest.

Evidence and caveats:
https://ccinternals.dev/cowork/plugins-and-plugin-root/?ref=gh-issue#fact-plugins-upload-a-personal-skill

Disclosure: that's an unofficial reference I maintain, based on reverse-engineering the shipped binaries and testing live sessions. It is not affiliated with Anthropic. Measured on Desktop 2.9939.2 / agent 2.1.281; behaviour served from Anthropic's side can change without a version bump.

wpilimon-arg · 17 days ago

there should be NO upload. Simply chatting and having Claude modify the
skill should suffice, cross app or cross access method. When Claude says,
"I have updated the skill so now it does this and that...", it should make
it available in full. This is AI, not sharing XLS files in 1998.

On Mon, Sep 28, 2026 at 7:26 PM Yaniv Golan @.***>
wrote:

yaniv-golan left a comment (anthropics/claude-code#20697) <https://github.com/anthropics/claude-code/issues/20697#issuecomment-5879859052> A data point on current builds, since part of this issue may have changed. A skill uploaded in the desktop app (Customize → Skills → + Add → Upload skill) became available in the Claude Code CLI as well as in local and cloud Cowork, within about a minute, each time with its full instructions, scripts and reference files. I measured this on one account on 2026-09-27, so it's worth confirming on yours. So for Desktop → CLI, one upload may now be enough. Two limits: - It's an upload. Every edit means uploading again, which is the drift @james-e-morris <https://github.com/james-e-morris> describes above. - It doesn't help the other direction. A skill that lives only in ~/.claude/skills still isn't seen by Cowork. For that, @tkkrixi <https://github.com/tkkrixi>'s git-marketplace recipe above is the route I'd use. Zip tip: put the skill's folder at the top level (my-skill/SKILL.md …), and leave out .claude-plugin/plugin.json. The uploader refuses a zip that contains a plugin manifest. Evidence and caveats: https://ccinternals.dev/cowork/plugins-and-plugin-root/?ref=gh-issue#fact-plugins-upload-a-personal-skill Disclosure: that's an unofficial reference I maintain, based on reverse-engineering the shipped binaries and testing live sessions. It is not affiliated with Anthropic. Measured on Desktop 2.9939.2 / agent 2.1.281; behaviour served from Anthropic's side can change without a version bump. — Reply to this email directly, view it on GitHub <https://github.com/anthropics/claude-code/issues/20697?email_source=notifications&email_token=CNDOUA3D7H3WURD3F4K7HUD5RLQSXA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKOBXHE4DKOJQGUZKM4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-5879859052>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/CNDOUA6XQGEDF7Z3LI7AIZL5RLQSXAVCNFSNUABFKJSXA33TNF2G64TZHM4TGNZSGUZTINZVHNEXG43VMU5TGOBVGIZTKMBUGAZ2C5QC> . Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS <https://github.com/notifications/mobile/ios/CNDOUA3Z3BNFTBQKD7UUO7D5RLQSXA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKOBXHE4DKOJQGUZKM4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJKTGN5XXIZLSL5UW64Y> and Android <https://github.com/notifications/mobile/android/CNDOUA76QUWTIDHUJ2NJEMD5RLQSXA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKOBXHE4DKOJQGUZKM4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>. Download it today! You are receiving this because you commented.Message ID: <anthropics/claude-code/issues/20697/5879859052 @.*** com>
zachmayer · 17 days ago

This has been fixed with skill and plugin sync

yaniv-golan · 17 days ago

@wpilimon-arg you're mostly right, and most of it already exists. I checked live on 2026-09-29 (Desktop 2.9939.4):

  • In a Cowork chat, "update my saved skill X to …" shows an Update skill card. Confirm it, and the new version reaches your other sessions and the CLI in about a minute.
  • An update replaces only the instructions and keeps the skill's scripts and reference files. So a skill you've uploaded once can be edited from then on just by chatting.

The gaps:

  • A skill created in chat is a single SKILL.md. Scripts or reference files still need one upload first (after that, chat edits keep them).
  • Skills belong to an organization. A skill saved while the app was in one org isn't visible or editable from a session in another.
  • Saving skills from chat can be turned off by an org admin or unavailable on some plans, so not everyone is offered it.

So in practice: upload once if the skill has scripts, then edit by chatting.

https://ccinternals.dev/cowork/plugins-and-plugin-root/?ref=gh-issue#fact-plugins-edit-a-skill-in-chat

(Same unofficial reference as my comment above; measured on one account.)

wpilimon-arg · 17 days ago

But if you use claude code app, e.g. in Mac Os, and you edit skills by
chatting, it will prepare and pack the updated skill, but it will refuse to
show you the Update Skill button. It should be as easy as showing the
button in both flavors of the app. Ridiculous.

On Mon, Sep 28, 2026 at 8:12 PM Yaniv Golan @.***>
wrote:

yaniv-golan left a comment (anthropics/claude-code#20697) <https://github.com/anthropics/claude-code/issues/20697#issuecomment-5880391435> @wpilimon-arg <https://github.com/wpilimon-arg> you're mostly right, and most of it already exists. I checked live on 2026-09-29 (Desktop 2.9939.4): - In a Cowork chat, "update my saved skill X to …" shows an Update skill card. Confirm it, and the new version reaches your other sessions and the CLI in about a minute. - An update replaces only the instructions and keeps the skill's scripts and reference files. So a skill you've uploaded once can be edited from then on just by chatting. The gaps: - A skill created in chat is a single SKILL.md. Scripts or reference files still need one upload first (after that, chat edits keep them). - Skills belong to an organization. A skill saved while the app was in one org isn't visible or editable from a session in another. - Saving skills from chat can be turned off by an org admin or unavailable on some plans, so not everyone is offered it. So in practice: upload once if the skill has scripts, then edit by chatting. https://ccinternals.dev/cowork/plugins-and-plugin-root/?ref=gh-issue#fact-plugins-edit-a-skill-in-chat (Same unofficial reference as my comment above; measured on one account.) — Reply to this email directly, view it on GitHub <https://github.com/anthropics/claude-code/issues/20697?email_source=notifications&email_token=CNDOUA5QVIP74Z2FNSPVVJD5RLV4FA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKOBYGAZTSMJUGM22M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-5880391435>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/CNDOUA6U2EZ3AUFONIO7V535RLV4FAVCNFSNUABFKJSXA33TNF2G64TZHM4TGNZSGUZTINZVHNEXG43VMU5TGOBVGIZTKMBUGAZ2C5QC> . Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS <https://github.com/notifications/mobile/ios/CNDOUAY3JWB4RAISHUHEKCT5RLV4FA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKOBYGAZTSMJUGM22M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJKTGN5XXIZLSL5UW64Y> and Android <https://github.com/notifications/mobile/android/CNDOUAY5UDKXXAHZAH3NVNL5RLV4FA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKOBYGAZTSMJUGM22M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>. Download it today! You are receiving this because you were mentioned.Message ID: @.***>
kentlundgren · 13 days ago

I use the same skill in Claude Code, Cowork and on my claude.ai account, and it ends up as three separate copies: ~/.claude/skills, the Cowork app folder, and the account. An edit in one does not reach the others. Today I had to update a Cowork skill via the "Update skill" card in a Cowork chat, because Claude Code could not push it to the account. A single shared skill store, or at least a visible sync from Claude Code, would save me a lot of work.

josie2479-maker · 10 days ago

+1, with a concrete case from today (Claude Max, Windows 11).

I keep one skill library and install it to two places: Cowork (server-side, via save_skill) and Claude Code (.claude/skills/). Neither surface can see or write the other's copy. Today I updated a skill in Claude Code, and the only way to get the same change into Cowork was to leave a note in a file asking a Cowork session to run save_skill by hand.

It's sold as one product, but in practice:

  • Skills don't sync between Chat, Cowork, Desktop and Claude Code, so every edit has to be installed twice, and the copies drift.
  • Tools are gated by surface. save_skill exists only in Cowork. Artifact, Agent (with a model choice), hooks and native file paths exist only in Claude Code. A skill written for one surface behaves differently on the other.
  • Scheduled tasks and routines are split the same way: local and cloud schedulers can't see each other.

What would fix it: one account-level skill store that every surface reads and writes, or at least a save_skill equivalent in Claude Code. A per-skill flag saying which surfaces it's meant for would also help.

Related: #53414 (siloed across tabs, closed), #77503, #83704, #31422.