From ecom
Bulk-archive dead/discontinued/zero-inventory products, remove ghost review stars, orphaned metafields, and HTML artifacts from Shopify product descriptions. Mutating cleanup after an audit.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ecom:shopify-catalog-cleanupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Fixing the debris an audit found. This is the mutating counterpart to
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.
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:
export SHOPIFY_STORE="your-store.myshopify.com"
export SHOPIFY_ACCESS_TOKEN="<your Admin API access token>" # env, not disk
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:
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.
Every recipe in references/recipes.md pairs a read-only count/preview query with the mutation it feeds. The discipline is not optional:
products(query:"…") with a filter returns
count and a page of titles. Read them.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.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.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.
Design the criteria before you touch anything. Common "dead" signals, combined with AND (a product is rarely dead on one signal alone):
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.
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:
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.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.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.
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.
metafieldsDelete,
description-artifact scan + safe rewrite.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 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):
# 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, metafieldsDelete. These skills capture operational lessons the docs don't; they are not a replacement for the reference.
npx claudepluginhub kgelster/awesome-ecom-skills --plugin ecomFinds product-data defects across a Shopify catalog: missing photos, thin descriptions, pricing anomalies, missing type/vendor. Read-only audit that ranks problems for sibling fix skills.
Manages Shopify product catalogs: products, variants, options, collections, metafields, metaobjects, inventory, bulk operations, taxonomy, media via GraphQL API.
Creates and manages Shopify products via GraphQL Admin API or CSV imports. Bulk import/update variants, inventory, images, assign to collections.