Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5251e71
feat(migrate): make clerk migrate generally available
royanger Oct 6, 2026
b30ddc0
docs: list clerk migrate in the README and match its help
royanger Oct 6, 2026
d2608a5
docs(changeset): Make clerk migrate available without CLERK_EXPERIMENTAL
royanger Oct 6, 2026
a47634c
docs(changeset): stop calling clerk migrate experimental in the earli…
royanger Oct 14, 2026
5aae9a9
fix(migrate): point migration doc links at the per-platform guides
royanger Oct 28, 2026
ddc5792
fix(migrate): point the custom-source link at the migration guide
royanger Oct 29, 2026
fce1f12
test(completion): check completion offers migrate and its subcommands
royanger Oct 30, 2026
e22c6a2
docs(changeset): add the Oxford commas to the migrate changesets
royanger Oct 30, 2026
370ed23
docs(migrate): say the CLI redacts a libsql token, in the active voice
royanger Oct 30, 2026
52c2c1a
refactor: keep the CLERK_EXPERIMENTAL gate, and document it
royanger Oct 30, 2026
17d3667
docs(migrate): say "auth platform" in the migrate help and README
royanger Oct 30, 2026
ae51184
docs(migrate): add the missing Oxford commas
royanger Oct 30, 2026
d72b59b
docs(migrate): say "auth platform" in the --reserve-unverified text
royanger Oct 30, 2026
176b6a7
docs(migrate): drop the save-location prompt from export clerk's example
royanger Oct 30, 2026
1ade536
docs(migrate): say --service-account takes the key's JSON too
royanger Oct 30, 2026
527d70d
docs(migrate): correct the dry-run exit codes, Firebase key errors, a…
royanger Oct 30, 2026
10fc7e7
docs(migrate): name --source in export clerk's comment
royanger Oct 30, 2026
d4e5bc5
docs(migrate): say Firebase phones import verified, and why
royanger Oct 30, 2026
a9ae69b
docs: match the experimental @example to the migrate stub
royanger Oct 30, 2026
8572283
docs(migrate): say a continued run stays in an import's JSON
royanger Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/migrate-custom-sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"clerk": minor
---

Add `clerk migrate sources`, and let `clerk migrate import --source` take the path to a source you wrote. `clerk migrate sources` lists the built-in sources with the passwords, MFA and metadata each brings across, and `clerk migrate sources <source>` shows where each field lands, how to export, and any caveats. A source you wrote is a `.ts` or `.js` file in your project, checked before use, so a platform with no built-in source can be imported without rebuilding the CLI. `clerk migrate` is still experimental and needs `CLERK_EXPERIMENTAL=migrate`.
Add `clerk migrate sources`, and let `clerk migrate import --source` take the path to a source you wrote. `clerk migrate sources` lists the built-in sources with the passwords, MFA, and metadata each brings across, and `clerk migrate sources <source>` shows where each field lands, how to export, and any caveats. A source you wrote is a `.ts` or `.js` file in your project, checked before use, so a platform with no built-in source can be imported without rebuilding the CLI.
2 changes: 1 addition & 1 deletion .changeset/migrate-reserve-unverified.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"clerk": minor
---

Add `clerk migrate import --reserve-unverified`, which creates the emails and phones a source never verified as reserved (usable for sign-in, locked to the user) instead of unverified. At a terminal, the import asks. `clerk migrate` is still experimental and needs `CLERK_EXPERIMENTAL=migrate`.
Add `clerk migrate import --reserve-unverified`, which creates the emails and phones an auth platform never verified as reserved (usable for sign-in, locked to the user) instead of unverified. At a terminal, the import asks.
2 changes: 1 addition & 1 deletion .changeset/migrate-user-limit.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"clerk": patch
---

Check `clerk migrate import` against the user limit Clerk reports for the instance, so a development instance Clerk has raised no longer rejects users it has room for. `CLERK_MIGRATE_DEV_USER_LIMIT` is now only a fallback for when Clerk doesn't report a limit, and `--json` says where the limit came from. `clerk migrate` is still experimental and needs `CLERK_EXPERIMENTAL=migrate`.
Check `clerk migrate import` against the user limit Clerk reports for the instance, so a development instance Clerk has raised no longer rejects users it has room for. `CLERK_MIGRATE_DEV_USER_LIMIT` is now only a fallback for when Clerk doesn't report a limit, and `--json` says where the limit came from.
9 changes: 9 additions & 0 deletions .changeset/migrate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"clerk": minor
---

Make `clerk migrate` available without `CLERK_EXPERIMENTAL=migrate`: it now shows in `clerk --help` and shell completion.

- `clerk migrate export` gets users out of Clerk, Supabase, Firebase, Auth0, WorkOS, Better Auth, or Auth.js into a run, and `clerk migrate import` checks every user against your instance, asks before it writes, and continues a run that stopped partway.
- `clerk migrate runs` shows what each run did, `clerk migrate undo` deletes the users an import created, and `clerk migrate sources` shows what each source brings across. `--source ./my-source.ts` imports with a source you wrote.
- Firebase phones import verified, because Firebase has no phone-verified field. `clerk migrate sources firebase` and the README say so.
12 changes: 12 additions & 0 deletions .claude/rules/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ When creating or modifying a command, evaluate whether it needs an agent mode. C

When adding or modifying a command, ensure tab-completion stays correct. See `.claude/rules/completion.md` for the full guide. Short version: use `.choices()` on arguments and options so completions are automatic; only touch `__complete.ts` when `.choices()` is not suitable.

## Shipping a command behind `CLERK_EXPERIMENTAL`

A command can merge to `main`, and reach `@canary` and releases, before it's ready, behind a name in `CLERK_EXPERIMENTAL`. `src/lib/experimental.ts` holds the gate: `isExperimentEnabled(name)` and `requireExperiment(name)`. `clerk migrate` shipped this way, one slice per PR.

To gate a command:

1. In its `register<Name>`, check `isExperimentEnabled(name, env)` before building the tree. When it's off, register a hidden stub instead, and return: `program.command(name, { hidden: true })` with `.helpOption(false)`, `.allowUnknownOption()`, `.allowExcessArguments()`, `.argument("[args...]")`, and an action that calls `requireExperiment(name, env)`. Override the stub's `helpInformation` to call `requireExperiment` too, so `clerk help <name>` refuses. The stub has no subcommands, so it stays out of `clerk --help` and completion, and every invocation exits 2 with `experiment_disabled`, naming the variable to set.
2. Set `CLERK_EXPERIMENTAL=<name>` in the command's tests and E2E runs.
3. Say so in its README and every changeset: the command is experimental and needs `CLERK_EXPERIMENTAL=<name>`.

To ship it, remove the stub and the tests' `CLERK_EXPERIMENTAL` setup, add the command to the root README's help output, and write a changeset that announces it. Drop the "experimental" sentence from any of its earlier changesets that haven't been released yet. Keep `experimental.ts` for the next gated command.

## Root README

`README.md` at the project root contains the CLI help output. When commands are added, removed, or their options change, update the help output in `README.md` to stay in sync. You can regenerate it by running `bun run src/cli.ts --help`.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Commands:
init [options] Initialize Clerk in your project
link [options] Link this project to a Clerk application
mcp Manage the Clerk remote MCP server connection for AI editors and CLIs
migrate Migrate users into Clerk from another auth platform or another Clerk instance
open Open Clerk resources in your browser
security Audit an instance against Clerk's security recommendations
telemetry Control CLI usage telemetry (status, disable, enable)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli-core/src/commands/init/scan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("detectAuthLibraries", () => {
runDetectAuthLibraries({ "next-auth": "5.0.0", next: "15.0.0" });
const output = captured.out;
expect(output).toContain("NextAuth");
expect(output).toContain("clerk.com/docs/migrations/nextauth");
expect(output).toContain("clerk.com/docs/guides/development/migrating/authjs");
});

test("detects Auth0 via @auth0/nextjs-auth0", () => {
Expand Down
18 changes: 9 additions & 9 deletions packages/cli-core/src/commands/init/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,32 @@ const AUTH_LIBRARY_SCANS: AuthLibraryScan[] = [
{
packages: ["next-auth"],
name: "NextAuth / Auth.js",
docsUrl: "https://clerk.com/docs/migrations/nextauth",
docsUrl: "https://clerk.com/docs/guides/development/migrating/authjs",
},
{
packages: ["@auth0/nextjs-auth0", "auth0"],
name: "Auth0",
docsUrl: "https://clerk.com/docs/migrations/auth0",
docsUrl: "https://clerk.com/docs/guides/development/migrating/auth0",
Comment thread
royanger marked this conversation as resolved.
Resolved
},
{
packages: ["@supabase/ssr", "@supabase/auth-helpers-nextjs"],
name: "Supabase Auth",
docsUrl: "https://clerk.com/docs/migrations/supabase",
docsUrl: "https://clerk.com/docs/guides/development/migrating/supabase",
},
{
packages: ["firebase"],
name: "Firebase",
docsUrl: "https://clerk.com/docs/migrations/firebase",
docsUrl: "https://clerk.com/docs/guides/development/migrating/firebase",
},
{
packages: ["passport"],
name: "Passport.js",
docsUrl: "https://clerk.com/docs/migrations/overview",
docsUrl: "https://clerk.com/docs/guides/development/migrating/custom-source",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clerk init now sends Passport and Kinde users to the custom-source docs page, but clerk migrate sources and the custom-source errors still link the GitHub README (CUSTOM_SOURCES_URL in sources/load-custom.ts). Move that to the docs page too, so both point at one guide?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already done in 08d4197, which hadn't been pushed when you reviewed. CUSTOM_SOURCES_URL now points at https://clerk.com/docs/guides/development/migrating/custom-source, so clerk init, clerk migrate sources, the --source help and every custom-source error link the same guide.

},
{
packages: ["better-auth"],
name: "Better Auth",
docsUrl: "https://clerk.com/docs/migrations/overview",
docsUrl: "https://clerk.com/docs/guides/development/migrating/better-auth",
},
{
// AuthKit only: `@workos-inc/node` also serves SSO, Directory Sync and
Expand All @@ -65,12 +65,12 @@ const AUTH_LIBRARY_SCANS: AuthLibraryScan[] = [
"@workos-inc/authkit-js",
],
name: "WorkOS",
docsUrl: "https://clerk.com/docs/guides/development/migrating/overview",
docsUrl: "https://clerk.com/docs/guides/development/migrating/workos",
},
{
packages: ["@kinde-oss/kinde-auth-nextjs"],
name: "Kinde",
docsUrl: "https://clerk.com/docs/migrations/overview",
docsUrl: "https://clerk.com/docs/guides/development/migrating/custom-source",
},
];

Expand Down Expand Up @@ -129,7 +129,7 @@ const CODE_SCANS: CodeScan[] = [
// out. The `sso` and `directorySync` modules alone hold no users.
pattern: "\\bworkos\\.userManagement\\b",
message: "WorkOS User Management still in use",
docsUrl: "https://clerk.com/docs/guides/development/migrating/overview",
docsUrl: "https://clerk.com/docs/guides/development/migrating/workos",
},
{
pattern: "getServerSession\\s*\\(",
Expand Down
Loading
Loading