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.
- Run
codex plugin marketplace list --jsonandcodex 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. - For a Git marketplace, run
codex plugin marketplace upgrade claude-skillswhen refreshing is requested. For this local checkout, runpython3 tools/sync_codex.pyfrom the repository root after source changes. - 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. Consultcodex plugin --helpif the installed CLI differs. Never fall back toclaude pluginor edit Codex's internal cache/registry by hand. - 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
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
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".
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.