A standalone, local-first CLI that inventories what an e-commerce store is made of — pages, sections, and the components inside them.
Point it at a Shopify store and it discovers a representative set of pages, captures each one in a real browser (HTML, screenshots, a structured DOM snapshot), and classifies the result against a fixed taxonomy — shop → pages → sections → components — into a versioned categorization.json.
Deterministic detectors do as much as they can; a configured AI model resolves what they cannot, and is measured and bounded per run. With no key at all it still produces an inventory, just a smaller one.
npx @weareikko/magma categorize https://example-store.comStatus:
categorizeis the product. The CRO-audit pipeline this repo started as — a scoredreport.jsonfrom a pack of CRO rules — was removed in 2026-08 rather than left in place unreachable; it was the original destination, but a second pipeline behind no entry point is an assumption about a shape nobody committed to.
Requires Node ≥ 24. No API key is needed — Playwright is the default engine and everything degrades gracefully without credentials.
npx @weareikko/magma categorize <url> # no install
npm install -g @weareikko/magma # or install the `magma` binary
magma doctor # check the local setupAn API key for any supported AI provider (Anthropic, OpenAI, OpenRouter, Gemini) unlocks the section/component classifier. Set it in the environment or run magma init to write a local config.
export ANTHROPIC_API_KEY=sk-…| Command | What it does |
|---|---|
magma categorize <url> |
Inventory a Shopify store — shop → pages → sections → components. Writes categorization.json. |
magma categorize <url> --url <u>… |
Inventory exactly these pages instead of discovering them. |
magma show [target] |
Browse the runs on disk in a browser — pages → sections → components, each next to its screenshot. |
magma init / magma doctor
|
Write local config / check the setup. |
Useful flags: --pages <n> caps discovery, --model <provider/id> overrides the configured one-shot classifier, --no-agent runs deterministic-only, --out <dir> sets the run store root, --config <file> points at a config explicitly.
Sections the deterministic detectors cannot resolve go to an agent that gathers its own evidence — it looks at the section, at where it sits on the full page, at its markup, and at the elements the capture recorded.
It runs on your own Claude Code installation whenever you have one. If claude is on your PATH, that is
what is used: no API key to set, nothing in .env. Without it, the agent falls back to ai.model and its
provider key, so the classifier still runs on a machine that has no Claude Code — CI, a container, a
colleague with an OpenRouter key.
| needs | when it is used | |
|---|---|---|
claude-code |
Claude Code on PATH
|
preferred whenever available |
pi-agent |
ai.model + a provider key |
fallback when Claude Code is absent |
magma doctor reports which one would run. --agent-backend <auto\|claude-code\|pi-agent> forces one —
useful for comparing them on a corpus, or reproducing a run recorded against a specific backend; a forced
backend that is not usable fails rather than quietly switching. MAGMA_CLAUDE_EXECUTABLE points at a specific
binary if you have several, and --no-agent turns the whole thing off.
On the claude-code backend each run is isolated from your own setup: no CLAUDE.md, no hooks, plugins,
skills or custom agents, none of your MCP servers, and no built-in tools — the agent's only capabilities are
the ones Magma serves it. That is what keeps the same store classifying the same way on two machines.
Runs are written to .magma/<site-slug>/runs/<ISO-timestamp>/. Capture writes every artifact to disk, so a run is fully re-readable afterwards without touching the network — which is what magma show reads.
magma show # every run in the store
magma show example-store.com # that site's newest runIt serves the store on http://localhost:8024 and opens a browser: a run list, then the inventory tree with each section's screenshot and evidence beside its label, a review queue of what the pipeline would not classify, and the run's provenance. Read-only — no network, no browser engine, no model calls.
Exit codes: 0 ok · 2 runtime error · 3 no usable page · 4 unsupported platform (non-Shopify).
This is an npm-workspaces monorepo.
| Package | Name | Published | Responsibility |
|---|---|---|---|
packages/cli |
@weareikko/magma |
✅ | The user-facing CLI: commands, config, progress UI, exit codes. Bin: magma. |
packages/core |
@weareikko/magma-core |
✅ | Step contract, in-process runner, run store + manifest, the discover/capture steps, the Playwright engine, providers. |
packages/rules |
@weareikko/magma-rules |
✅ | The categorization taxonomy and schema, the categorize step and its classifiers, the agent runtime. |
Dependency direction: cli → core + rules, rules → core. No cycles.
Requires Node ≥ 24.
Copy .env.example to .env to set local secrets (AI provider key, capture engine keys) — it's auto-loaded by npm test/npm run magma -- and gitignored; everything in it is optional.
npm install # install the workspace
npm run dev:ui # the `magma show` client with hot reload, against ./.magma
npm run magma -- doctor # run the CLI from source via vite-node
# note: this runs with cwd packages/cli/, so pass --config "$PWD/magma.config.ts"
# and an absolute --out when you want the repo-root config and run store
npm test # vitest across all packages
npm run typecheck # tsc --noEmit per package
npm run lint # oxlint
npm run build # vite library build (all packages)Versioning is a local step; publishing is CI's. Describe every user-visible change in a changeset as you go, then release when you want to:
npm run changeset # describe a change (patch/minor/major + a summary)
npm run version-packages # consume the changesets: bump, write CHANGELOGs, refresh the lockfile
git commit -am "chore(release): version packages" && git pushPushing that to main is what publishes: CI builds and runs changeset publish, which sends up any version npm does not already have, with provenance via OIDC trusted publishing. There is no npm token. A push carrying no version bump is a no-op, so ordinary commits are safe.
Functional Source License, Version 1.1, ALv2 Future License (FSL-1.1-ALv2) — source-available: use, modify and redistribute it for any purpose except competing with us, and each version becomes Apache 2.0 two years after release.