# Pluginsync

> Refresh the locally-installed Claude Code plugin marketplaces so newly released skill versions are actually on disk. Use when the user says "update my marketplace", "refresh my skills", "update the claude-skills plugins", "am I on the latest skills", "install the new skill", or "why is /skillfactory still the old version". Reports installed vs available version per plugin, installs what is missing, updates what drifted, flags plugins shadowed by a stale personal copy in ~/.claude/skills, and always says whether a restart is still needed.

- Skill: `natejswenson/pluginsync` (Agent Skill, multi-file: 39 files)
- Install (CLI): `npx skillmds@latest add natejswenson/pluginsync`
- Raw SKILL.md: https://api.skillmd.com/api/skills/natejswenson/pluginsync/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: natejswenson (https://skillmd.com/u/natejswenson)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/natejswenson/pluginsync

---


## Codex runtime

When running in Codex, invoke this skill as `$pluginsync`. Resolve scripts, assets,
and references from the directory containing this SKILL.md, regardless of the
current working directory. Existing `~/.claude/` personal-data paths remain valid
and are still used by the bundled scripts; they do not require Claude to run.
Map `Read`/`Write`/`Edit`/`Bash` to the available file and shell tools, and
`WebSearch`/`WebFetch` to available web tools. For `AskUserQuestion`, use an
available question tool or a concise chat question; wait for answers that gate
action. Use Codex's delegation tools for required subagents when available;
otherwise disclose that independent execution is unavailable. Discover connected
apps by capability rather than assuming Claude MCP tool names exist.

## Codex plugin updates

In Codex, use this route instead of the Claude CLI workflow below. The bundled
`scripts/pluginsync.js` manages Claude installations only.

1. Run `codex plugin marketplace list --json` and
   `codex plugin list --available --json --marketplace claude-skills`.
   Inspect the returned schema and report available and installed versions
   separately. Do not infer installation from marketplace membership.
2. For a Git marketplace, run `codex plugin marketplace upgrade claude-skills`
   when refreshing is requested. For this local checkout, run
   `python3 tools/sync_codex.py` from the repository root after source changes.
3. Install or refresh requested plugins with
   `codex plugin add <name>@claude-skills --json`, then re-read the list and
   installed manifest. If the version/content did not move, report it as stalled.
   Consult `codex plugin --help` if the installed CLI differs. Never fall back to
   `claude plugin` or edit Codex's internal cache/registry by hand.
4. Report what is on disk and ask the user to start a new Codex session to verify
   loading. A successful install does not prove the current session reloaded.

A refresh removes no plugins or personal skills. Inspect duplicate skill
entrypoints in `~/.agents/skills` and `~/.codex/skills` without deleting user data.

# /pluginsync — the marketplace on disk, not the one you assume

You are running the **pluginsync** skill. It reconciles the plugins installed on
this machine with what the marketplace actually offers.

**Announce at start:** "I'm using the pluginsync skill to reconcile your installed plugins with the marketplace."

> Commands below run from the directory containing this `SKILL.md` (`$SKILL_DIR`).
> Resolve it once.

## The one rule

**A plugin is not refreshed until the new version is read back off disk, and it
is not live until Claude Code restarts — report those as three different states
and never collapse them into one.**

Every command in this flow exits 0 whether or not anything moved. `claude plugin
update` prints no version and returns success when it no-ops. So "✓ updated
skillfactory" followed by a `/skillfactory` that still runs the old version is not a rare edge
case — it is the default failure, and it is indistinguishable from success
unless the report keeps the three states apart:

| State | Means | How it is known |
|---|---|---|
| **available** | the marketplace offers this version | `plugin.json` at the marketplace source |
| **on disk** | this version is installed here | `claude plugin list --json`, re-read after every write |
| **live** | this version is loaded in the running session | only true after a restart — never assert it |

**Never say a plugin is updated because a command succeeded.** Say it because
the version on disk changed. If it did not change, the row is `stalled`, and
`stalled` is reported as loudly as a failure.

## What is code and what is judgment

The split is declared in `skill-invariants.json` and checked — a deterministic
step whose command does not exist fails `skillfactory verify`.

| Deterministic — the machine decides | Command |
|---|---|
| resolve every marketplace's install location and each plugin's available version from plugin.json on disk | `node scripts/pluginsync.js check --no-fetch` |
| diff installed against available and classify each row | `node scripts/pluginsync.js check` |
| install/update each drifted plugin and read the resulting version back off disk | `node scripts/pluginsync.js apply` |

| Model judgment — nothing on disk answers it | Why |
|---|---|
| decide whether an orphan or disabled plugin is deliberate or rot | nothing on disk records intent — a disabled project-scoped plugin is normal, a disabled user-scoped one is usually a forgotten experiment |
| decide what to do about a plugin shadowed by a personal copy in ~/.claude/skills | the shadowing directory sometimes holds user data alongside the stale SKILL.md, so deleting the directory and deleting the shadow are different actions with different consequences |
| explain a stalled row | the CLI exits 0 either way, so why a version did not move is a diagnosis, not a field |

## The flow

### 1. Check — never ask what you can read

```bash
node scripts/pluginsync.js check
```

One row per plugin, and it answers everything a question would. **Never ask
about anything in it** — which plugins exist, what is installed, what drifted
and what is shadowed are all facts. A confirmation is not a question.

`--no-fetch` skips the marketplace refresh and reads disk only.
`--marketplace <name>` targets a marketplace other than `claude-skills`.

Show the table as the script printed it. Do not re-summarise it in prose, and do
not omit the `ok` rows — "these eleven are already current" is the answer to
half the reasons someone runs this.

### 2. Ask once, only if there is something to decide

If every row is `ok`, say so and stop; there is nothing to ask. Otherwise ask
one question — whether to apply — and only that. Three row types need a
judgment call and none of them are the script's to make:

| Row | What it means | What to say |
|---|---|---|
| `orphan` | installed, no longer offered by the marketplace | Ask before removing. **Never uninstall an orphan on your own** — it is more likely a plugin the user still wants than a mistake. |
| `disabled` | installed and current, but switched off | Usually deliberate for project-scoped plugins. Mention it once; do not re-enable it unprompted. |
| `shadowed` | `~/.claude/skills/<name>/SKILL.md` wins over the plugin | Deleting the *directory* and deleting the *shadow* differ — that directory sometimes holds user data. Offer to remove only the `SKILL.md`. |

### 3. Apply

```bash
node scripts/pluginsync.js apply
```

Installs and updates every drifted row, then re-reads the installed list and
compares. Report the table it prints, including any `stalled` row, and never
soften one into a success.

### 4. Say what is live

Close with the restart, always. The user's next `/skillfactory` runs the old version
until Claude Code restarts, no matter how clean the table looked.

## Commands

| Command | Returns |
|---|---|
| `pluginsync check` | one row per plugin — Plugin, Installed, Available, Action (`ok`/`update`/`install`/`orphan`/`disabled`/`error`) — plus a warning per shadowing personal skill, and a footer counting what would change |
| `pluginsync apply` | the same rows re-read after every write — Plugin, Was, Now, Outcome (`installed`/`updated`/`stalled`/`failed`) — where `stalled` means the command exited 0 and the version on disk did not move |

Both take `--json` for the structured payload, and `--home` / `--installed-json`
so the evals can run offline.

## Rules that are not negotiable

- **A plugin is not refreshed until the new version is read back off disk, and it is not live until Claude Code restarts — report those as three different states and never collapse them into one.**
- **Never claim a result you did not observe.** Say what you verified and what
  you did not.
- **Never report a stalled row as updated.** The command exiting 0 is not
  evidence; the version on disk is the only evidence.
- **Never uninstall an orphan without being asked.** A refresh removes nothing.
- **An unreadable source is an error row, never a dropped one.** A plugin the
  tool could not read must never be summarised inside "everything matches".

<!-- press:runtime -->
In Claude Code, load `/press`; in Codex, load `$press`; then follow the shared PRESS terminal/UI contract from `brand/agent-ui.md`. Do not copy or override that contract here.
<!-- press:runtime -->

