Skip to content
Back to skills

Shopify Catalog Cleanup

ASecurity

Use when fixing the debris an audit found in a Shopify catalog: bulk-archive dead/discontinued/zero-inventory products, clean up catalog debris, kill ghost review stars (empty star ratings a theme still renders after a review app was uninstalled), remove orphaned metafields left by an uninstalled app, strip HTML artifacts from product descriptions (double-nested bold, fully-bold paragraphs, empty tags), or fix messed-up / mangled product descriptions in bulk. Mutating cleanup work: archive vs...

  • 44 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added August 31, 2026
ai-agentsrustgobashnodeapi

Works with

  • claude code
  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned August 31, 2026

npx -y skills add kgelster/awesome-ecom-skills --skill shopify-catalog-cleanup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Shopify Catalog Cleanup?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Shopify Catalog Cleanup
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kgelster-shopify-catalog-cleanup/badge)](https://www.skillsdirectory.com/skills/kgelster-shopify-catalog-cleanup)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: shopify-catalog-cleanup
description: >-
  Use when fixing the debris an audit found in a Shopify catalog: bulk-archive
  dead/discontinued/zero-inventory products, clean up catalog debris, kill ghost
  review stars (empty star ratings a theme still renders after a review app was
  uninstalled), remove orphaned metafields left by an uninstalled app, strip HTML
  artifacts from product descriptions (double-nested bold, fully-bold paragraphs,
  empty tags), or fix messed-up / mangled product descriptions in bulk. Mutating
  cleanup work: archive vs delete, metafieldsDelete, description rewrites. Not for
  FINDING what's wrong (use shopify-catalog-audit) or filling missing metadata
  like SEO titles and alt text (use shopify-seo-metadata).
compatibility: >-
  Requires Shopify Admin API access (custom-app token or Shopify CLI 3.x).
  GraphQL written against Admin API 2025-07. Minimum scopes: read_products,
  write_products (products carry metafield read/write).
---

# Shopify Catalog Cleanup

Fixing the debris an audit found. This is the mutating counterpart to
`shopify-catalog-audit` (which finds problems) and `shopify-seo-metadata` (which
fills missing metadata). Everything here writes to the catalog, so the whole
skill is built around one rule: **preview the count, then mutate.**

## Store access

**Lane A: custom-app token (scriptable).** In Shopify admin: Settings → Apps
and sales channels → Develop apps → create an app → grant `read_products` and
`write_products`, install, copy the Admin API access token. Export it; never
write it to a committed file:

```bash
export SHOPIFY_STORE="your-store.myshopify.com"
export SHOPIFY_ACCESS_TOKEN="<your Admin API access token>"   # env, not disk
```

```bash
curl -s "https://$SHOPIFY_STORE/admin/api/2025-07/graphql.json" \
  -H "X-Shopify-Access-Token: $SHOPIFY_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ shop { name } }"}'
```

**Lane B: Shopify CLI OAuth (no stored token).** `shopify store auth --store
$SHOPIFY_STORE --scopes read_products,write_products` then `shopify store
execute`. Good for token-less stores where the owner logs in interactively.

**Toolkit preflight.** Lane B rides on the Shopify CLI: run `shopify version`
first and install it if missing. For the full Admin GraphQL schema and
validated execution, pair this skill with Shopify's official AI toolkit
plugin. In Claude Code, check `claude plugin list`; if it isn't there:

```bash
claude plugin marketplace add Shopify/Shopify-AI-Toolkit
claude plugin install shopify-plugin@shopify-ai-toolkit
```

Recommended, not required: Lane A needs only curl and a token. The toolkit
gives your agent the API; this skill gives it the playbook.

## The one rule: preview count, then mutate

Every recipe in [references/recipes.md](references/recipes.md) pairs a read-only
count/preview query with the mutation it feeds. The discipline is not optional:

1. **Run the count first.** `products(query:"…")` with a filter returns
   `count` and a page of titles. Read them.
2. **Sanity-check the number against expectation.** If you expected ~40 dead
   products and the count says 900, **stop and re-scope**: your filter is
   wrong, not the store. A count far above expectation is a bug in your query.
3. **Only then mutate,** and prefer the reversible path (below).

## Non-destructive doctrine

- **Archive, never delete.** Archiving a product (`status: ARCHIVED`) hides it
  from the storefront and channels but keeps the record, its handle, its history,
  and its URL-redirect potential. It is fully reversible: flip status back to
  `ACTIVE` or `DRAFT`. Deletion is not reversible: the product, its variants,
  and its metafields are gone. Default to archive; only delete when the owner
  explicitly asks and understands it is permanent.
- **Tag what you touch.** When you bulk-archive, add a dated tag
  (`archived-2026-07-05`) in the same mutation. That tag is your undo list: to
  reverse, query `tag:archived-2026-07-05` and set status back. Without it you
  cannot cleanly separate your batch from products that were already archived.
- **Read before you write.** A read query is cheap; an unscoped mutation is not
  reversible. Verify a write landed by re-reading the object through the Admin
  API, not the storefront (the storefront is CDN-cached and lies about
  freshness).
- Never print or commit the Admin API access token.

## Recipes, not scripts

The GraphQL blocks in the reference are copy-and-adapt starting points: your
agent writes throwaway code per engagement against the current schema, runs it,
discards it. Bulk mutations should batch and respect throttling: read the
`throttleStatus` in the `extensions` block and back off when
`currentlyAvailable` drops, rather than hammering a fixed rate.

## Three fix classes

### 1. Bulk-archiving dead products

Design the criteria before you touch anything. Common "dead" signals, combined
with AND (a product is rarely dead on one signal alone):

- Zero total inventory across all variants, and
- Zero sales in a trailing window (e.g. 12 months), and
- Created/updated long enough ago to not be a new launch, and
- Not in a collection you care about, or already tagged discontinued.

Turn that into a `products(query: …)` search filter, run the **count** query,
read the sample titles, confirm the number is in the ballpark you expected, then
run `productUpdate` to set `status: ARCHIVED` and append the dated tag. The
unarchive path is the same query on your tag, flipping status back. Recipes:
[references/recipes.md](references/recipes.md).

### 2. Orphaned metafield debris (the ghost-star class)

Classic symptom: a store uninstalled a product-review app (Shopify's own Product
Reviews is the usual culprit), but the theme still renders **empty star ratings**
on every product. The app left its metafields behind: the `spr.reviews` field the
Product Reviews app wrote, plus a `reviews` namespace holding `reviews.rating` and
`reviews.rating_count`. The theme's star snippet reads them, finds `0`, and paints
five empty stars.

Fix safely:

1. **Confirm the app is truly gone.** Check Settings → Apps; the namespace must
   belong to an app that is uninstalled, not merely paused. Deleting metafields
   an installed app owns will just have the app rewrite them.
2. **Inventory the orphaned namespaces.** Query the product metafield
   definitions and a sample of products for every suspect namespace (`spr` and
   `reviews` for the Product Reviews app) so you know exactly which
   `namespace.key` pairs you are removing and how many products carry them.
3. **Delete via `metafieldsDelete`**, keyed by owner GID + namespace + key,
   covering both `spr.reviews` and the `reviews.rating` / `reviews.rating_count`
   fields. Preview the affected-count first, same as archiving; batch the
   identifiers to stay under rate/cost limits.
4. **Re-read** a few products to confirm the fields are gone, then reload the
   storefront product: the empty stars should be gone (the theme snippet may
   also need its now-dead reference removed; flag that to whoever owns the theme).

**Matrixify warning:** a blank cell in a Matrixify update **deletes** that
metafield, it does not skip it. That is a footgun for accidental deletion and a
tool for deliberate bulk removal: either way, know which you are doing. For
spreadsheet-driven bulk work and that trap, see `shopify-matrixify`.

### 3. HTML artifacts in descriptions

Editors, app injections, and copy-paste leave residue in `descriptionHtml`:
double-nested `<strong><strong>…`, whole paragraphs wrapped in bold, empty
`<p></p>` / `<span></span>` tags, stray inline styles. Detect cheaply with an
index scan (pull `descriptionHtml` for the scope, regex for the artifact
patterns) rather than eyeballing products one by one. Fix by rewriting
`descriptionHtml` per product via `productUpdate`: a targeted string transform,
not a from-scratch rewrite that would lose real formatting.

**Log old and new for every rewrite.** As you go, write each rewrite (product
id, old `descriptionHtml`, new `descriptionHtml`, status) to a CSV, the same
discipline `shopify-seo-metadata` mandates for meta writes. That log is two
things: your diff review (read a sample of old→new pairs before declaring
done) and your only undo list. Archiving gets a dated tag to query for
reversal; a rewrite has nothing, so without the log a bad transform is
unrecoverable. This matters downstream too: skills that generate from body
HTML (SEO meta, for one) inherit whatever a bad rewrite left behind.

**Post-edit scan doctrine.** After ANY bulk content edit, sweep the affected
scope again for residue: double-bold, orphaned tags, half-applied
transforms, **before** you declare done. A bulk regex fix routinely leaves a
new artifact on the 3% of products whose markup didn't match your assumption.
The passing re-scan is the evidence; "the transform ran" is not.

## References

- [references/recipes.md](references/recipes.md): count-before-mutate archive
  query + mutation, metafield-namespace inventory + `metafieldsDelete`,
  description-artifact scan + safe rewrite.

## Provenance and maintenance

Last verified: 2026-07-05. GraphQL pinned to Admin API 2025-07; Shopify
deprecates versions on a rolling quarterly schedule, so verify against
[shopify.dev](https://shopify.dev/docs/api/admin-graphql) before trusting a
version-specific claim. Read-only re-verification a stranger can run (a
metafield-namespace inventory and an archived-product count, both non-mutating):

```bash
# how many products are already archived, and a metafield namespace sample
curl -s "https://$SHOPIFY_STORE/admin/api/2025-07/graphql.json" \
  -H "X-Shopify-Access-Token: $SHOPIFY_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ productsCount(query:\"status:archived\") { count } products(first:5) { nodes { title metafields(first:20) { nodes { namespace key } } } } }"}'
```

Canonical docs: [productUpdate](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productupdate),
[metafieldsDelete](https://shopify.dev/docs/api/admin-graphql/latest/mutations/metafieldsdelete).
These skills capture operational lessons the docs don't; they are not a
replacement for the reference.

Files in this skill

  • SKILL.md10 KB
  • references/recipes.md6.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.