← All guides10 min read

Using the Graftport CLI with Claude Code: install to first publish

Set up the Graftport CLI and migration-engineer skill in Claude Code, then drive a real Shopify migration through the validate and publish loop without leaving the terminal.

Claude Code is the cleanest fit for the Graftport CLI today: it has a first-class shell tool, it natively reads Anthropic's skill format, and the skill install command lands the migration-engineer playbook in exactly the directory Claude looks at. This guide is the install-to-first-publish walkthrough for that combination — a step short of a screenshot tour, opinionated enough that you can copy and paste it.

If you want the conceptual overview of why an AI agent suits migration mapping in the first place, read the pillar AI Shopify migration with a coding agent first. This guide assumes you have already created the migration in the Graftport app — the wizard part is unchanged.

1. Install the CLI

The CLI is a Python package. The recommended install uses uv, which keeps the tool isolated from any other Python environment on your machine:

uv tool install graftport

Plain pip install graftport also works if you prefer. After install, verify:

graftport --version
# graftport 0.1.0

The package is small and the install is a one-liner; uv tool uninstall graftport (or pip uninstall graftport) reverses it cleanly.

Installing from a checkout

Before the PyPI release, install directly from the monorepo with an editable install so local edits are picked up without re-installing:

uv tool install --editable ./graftport

pipx install --editable ./graftport and pip install -e ./graftport are the equivalent commands for those toolchains.

2. Sign in

The CLI uses the same browser-based OAuth flow you would recognise from gh auth login --web, vercel, or supabase login. The hosted Graftport URLs are baked into the wheel, so the only thing the login needs is your browser:

graftport auth login

Under the hood, the CLI picks a free 127.0.0.1 port, starts a one-shot HTTP server on it, opens your browser to <app-url>/cli-auth?state=…&port=…, and waits for the web app to POST the resulting Supabase session back. The session is written to ~/.graftport/config.json along with the refresh token, so subsequent CLI calls silently mint a new access token when the current one expires. You normally only re-run auth login when the refresh token itself expires or you explicitly auth logout.

To check the result:

graftport auth status

If you are on a server without a browser, pass --no-browser to print the URL and open it from another machine; the CLI keeps listening on the same loopback port.

3. Install the skill

The skill is a Markdown document that teaches a coding agent how to use the CLI: the iterative workflow, the validation error codes the validator returns, and the JSONata patterns that fix each one.

For Claude Code, one command lands it in the right place:

graftport skill install --pretty

That auto-detects which agents are present on your user account and writes the skill to the conventional location for each. For Claude Code (and Claude Desktop — Anthropic unified the on-disk skill location, so one install serves both products), the skill lands at:

~/.claude/skills/graftport-migration-engineer/

The skill is user-global, not per-project. Install it once on your machine and every Claude Code session, in every working directory, picks it up.

About the older skill name

An earlier iteration of the same skill shipped under the name iterating-graftport-mappings. On install for Claude, a stale ~/.claude/skills/iterating-graftport-mappings/ directory is removed automatically — but only when its SKILL.md frontmatter confirms it is the old graftport skill. If you happen to have an unrelated skill sitting at that path, it is left alone.

Targeted installs

If you want to install for only one agent, or force-overwrite a customised file:

graftport skill install --for claude     # just Claude (Code + Desktop)
graftport skill install --for all        # write every supported target
graftport skill install --force          # overwrite a customised file

For agents the install command does not target — Cursor, GitHub Copilot, AGENTS.md — run graftport skill > skill.md and paste the file into whatever your agent reads. Same skill text; one extra step.

4. The first session in Claude Code

Open Claude Code in any working directory. You do not need to be inside a Git repository — the CLI keeps its state in ~/.graftport/config.json, not the project. A fresh Claude Code session is fine.

Tell Claude what you want, in the same tone you would use with a junior migration engineer:

Take a look at the product mapping on Graftport migration mig_8f2a and clean up any field issues you find against a sample of 50 rows. Don't push to Shopify; I'll approve the run separately.

The skill teaches the agent to start with discovery — it runs graftport migrations show mig_8f2a to confirm the migration exists, graftport mappings list mig_8f2a to find the product mapping ID, and graftport source rows mig_8f2a product --limit 5 to see real source samples. It does not guess at the schema.

5. The validate / publish loop, end to end

Here is the loop in commands, the way Claude Code will run them.

Pull the current mapping into a local file:

graftport mappings show <mapping_id> --raw > current.jsonata

Validate the current state against a sample:

graftport mappings validate <mapping_id> \
    --jsonata current.jsonata \
    --limit 50 --pretty

If validation is clean already, the agent stops and tells you. If it fails, the response is a list of per-row errors with structured codes. The agent edits current.jsonata to address each one, then re-runs the validate command. This loop is the agent's bread and butter — short, deterministic, with a clear "this passes" signal.

Publish a new version:

graftport mappings publish <mapping_id> \
    --jsonata current.jsonata \
    --notes "fix AMOUNT_MISMATCH on weight + status enum"

mappings publish is agent-allowed: no --yes is required because the write is metadata-only. It bumps the mapping version and flags downstream records for re-load on the next run. The next run is itself gated, so publishing freely is safe.

Dry-run end-to-end:

graftport runs start <migration_id> --dry-run

Also agent-allowed. The dry-run computes every payload Shopify would receive without pushing it. Zero load cost. You inspect the result in the Data tab in the app and decide whether to ship a real run.

For the mapping-side mechanics in more depth — which JSONata patterns solve which error codes, when to split mappings across multiple versions — read Automating JSONata mappings with AI.

6. The human-gated step

When the dry-run looks right, the agent will compose the real run command and stop. In a Claude Code session it looks like this:

graftport runs start mig_8f2a

The CLI prints the resolved action and a live cost estimate to stderr, then blocks waiting for an interactive yes. The bundled skill is explicit that the agent must not pass --yes to bypass the gate. You read what is about to happen, type yes, and the run starts. If you type anything else, the command exits with code 3 — distinct from a failure (1) — so the agent and your wrapping scripts can tell "you declined" apart from "something broke."

For the full design rationale behind the two-tier model, see Human approval gates for AI Shopify migrations.

7. Headless / CI

The same CLI runs in CI without a browser. If you already have a Supabase JWT (a service account token from your CI secrets manager, for example), pass it directly on login and skip the browser flow:

graftport auth login \
    --postgrest-url https://<your-project>.supabase.co/rest/v1 \
    --apikey        "$YOUR_PUBLIC_ANON_KEY" \
    --validate-url  https://<your-fastapi-deploy>.example.com \
    --access-token  "$YOUR_JWT"

Configs written this way carry no refresh token, so you re-run auth login when the JWT expires. CI is also the one place where it is reasonable for a human operator to script --yes onto gated commands — but only when a human wrote the script and approved the batch up front. The agent itself still does not pass it.

8. Pointing at a local stack

If you are running a local Graftport stack — Supabase on :54321, FastAPI on :8000, Next.js on :3000 — point the login at the dev URLs explicitly:

graftport auth login \
    --postgrest-url   http://127.0.0.1:54321/rest/v1 \
    --apikey          "$NEXT_PUBLIC_SUPABASE_ANON_KEY" \
    --validate-url    http://127.0.0.1:8000 \
    --app-url         http://127.0.0.1:3000

The browser flow opens <app-url>/cli-auth, waits for sign-in, and writes the session to ~/.graftport/config.json. The Supabase JWT carries the app.tenant_id claim so RLS scopes everything to your dev tenant.

The same flags accept production overrides if you self-host Graftport, and the corresponding GRAFTPORT_DEFAULT_* environment variables work for any of them.

9. What you have now

After these eight steps you have:

  • The Graftport CLI installed and authenticated.
  • The migration-engineer skill installed for Claude Code and Claude Desktop.
  • A session that knows how to investigate source data, validate JSONata mapping changes, publish new mapping versions, and propose dry-runs without spending anything.
  • A clean boundary between what the agent does on its own and what it brings to you for approval.

The conceptual overview lives at /ai-migrations — the install commands, the agent matrix, and the FAQ in one place.

Related reading

Once your Claude Code session is producing clean validates and green dry-runs, the rest of the migration is the same loop on the other resource types. Start one at app.graftport.com/signup or read the /ai-migrations overview.

Ready to migrate?

Connect a source store, dry-run a migration, see the exact Shopify result before a single record lands. The same platform your team will use on go-live night.

Get started See the calculator
Related guides
Human approval gates for AI Shopify migrations: the Graftport contract
Why an AI coding agent should never push to Shopify on its own, and how Graftport's two-tier CLI contract enforces a human gate on every cos
Automating JSONata Shopify mapping with an AI coding agent
How the Graftport CLI lets an AI agent investigate source rows, fix JSONata mapping errors against structured validation codes, and publish
AI Shopify migration with a coding agent: the Graftport playbook
How to run an AI Shopify migration end to end with Claude Code, Cursor, Windsurf, or Copilot driving the Graftport CLI, with the costly step