fix(init): simplify auth/keyless flow and fix --starter - #175

Merged
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes
Apr 20, 2026
Merged

fix(init): simplify auth/keyless flow and fix --starter#175
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Four clerk init fixes on this branch:

  • Skip askSkipAuth and keyless flow when signed in. If the user has an OAuth token, clerk init goes straight to the authenticated flow (auth + link + env pull).
  • Count CLERK_PLATFORM_API_KEY as authenticated. Non-interactive / CI runs with an API key no longer fall through to keyless mode. A new isAuthenticated() helper covers both OAuth tokens and the env var.
  • Remove the "Skip authentication for now?" prompt. The prompt added a step without meaningful choice — the unauthenticated path can't pull keys either way. Now the flow just follows auth state: signed in → authenticated flow + env pull; not signed in → keyless automatically (the keyless info message already points users to clerk auth login for later).
  • Keep clerk init --starter interactive without --framework. Running --starter without -y no longer errors with "Non-interactive mode requires --framework" — it only implies "yes, bootstrap," not "skip every prompt."

Also suppresses the "keyless not supported" framework message outside the bootstrap flow.

Test plan

  • bun run test — init suite covers the permutations (auth vs. not, API key vs. OAuth, bootstrap vs. existing repo, --starter with and without -y)
  • bun run lint / bun run typecheck
  • CI E2E suite passes (was failing on Astro / Next / TanStack / React Router before the API-key fix)
  • Manual: clerk init on a keyless framework while signed in → authenticated flow, no keyless prompt
  • Manual: clerk init on a keyless framework while signed out → keyless flow, no prompt
  • Manual: clerk init in an existing repo with keyless framework while signed in → no keyless prompt, env pulled
  • Manual: clerk init --starter (no -y) → interactive framework picker instead of an error

@changeset-bot

changeset-botBot commented Apr 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea3b111

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rafa-thayto has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fcab2f5-4b16-4323-8e0f-2e9d72b71f17

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70430 and ea3b111.

📒 Files selected for processing (7)
  • .changeset/more-init-fixes.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/bootstrap.test.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/heuristics.ts
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
📝 Walkthrough

Walkthrough

The init command flow was changed to check authentication explicitly before skipping prompts. resolveKeylessMode now returns false when already authenticated and only enables keyless for unauthenticated, keyless-capable frameworks; it avoids logging “keyless not yet supported” unless bootstrap is requested. handleStarter and bootstrap calls now pass an implicitBootstrap flag instead of skipConfirm to bypass only the initial “create a new one?” confirmation. The exported askSkipAuth helper was removed and promptAndBootstrap gained an implicitBootstrap option. A new heuristics.isAuthenticated() helper was added (checks CLERK_PLATFORM_API_KEY or stored auth). Tests were updated to use isAuthenticated and to expand keyless/auth coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description check✅ PassedThe pull request description clearly and comprehensively explains the changes, including four specific fixes to the clerk init command with concrete examples and a detailed test plan.
Title check✅ PassedThe title accurately summarizes the main changes: fixing authentication/keyless flow logic and resolving the --starter flag issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417120320
PackageVersion
clerk0.0.3-snapshot.v20260417120320

Published from 5cf3c19

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 5cf3c19 to 7d45a72CompareApril 17, 2026 13:21
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417132423
PackageVersion
clerk0.0.3-snapshot.v20260417132423

Published from 7d45a72

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417160442
PackageVersion
clerk0.0.3-snapshot.v20260417160442

Published from 9a70430

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 9a70430 to 1682ce6CompareApril 17, 2026 16:54
@rafa-thaytorafa-thayto changed the title fix(init): check auth before keyless/skip-auth promptsfix(init): simplify auth/keyless flow and fix --starterApr 17, 2026

@wyattjohwyattjoh left a comment

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.

Code Review — PR #175

Reviewed with Opus + Codex second-opinion validation.

Major

M1. Removing the bootstrap != null guard broadens keyless auto-select to existing projects(codex partial)
packages/cli-core/src/commands/init/index.ts:215-225 now returns keyless = true for any unauthenticated user on a keyless-capable framework, not only during bootstrap. Downstream effects:

  1. authenticateAndLink is skipped when users re-run clerk init signed out on an existing Clerk project.
  2. ctx.keyless = true at frameworks/helpers.ts:204-221,461-467 toggles permissive clerkMiddleware() (no route protection). Idempotency via hasClerkImport should prevent overwrites, but a scaffold-idempotency test for existingClerk: true + keyless: true would make the invariant load-bearing rather than incidental.
  3. env pull is skipped for the keyless branch.

The changeset does note keyless auto-select behavior, but the existing-project qualifier is not spelled out. Consider either restoring the if (!bootstrap) return false guard, or explicitly documenting the existing-project case in README and changeset.

M2. isAuthenticated() conflates "no credentials" with "network/API failure"(codex partial)
packages/cli-core/src/commands/init/heuristics.ts:139-147 delegates to getAuthenticatedEmail() which catches all errors and returns null. Expired tokens, Clerk 5xx, or offline laptops all silently demote the user into keyless (for keyless-capable frameworks) or skip-auth. Previously askSkipAuth gave the user a decision point. Consider a lighter credential-presence check (getToken() + CLERK_PLATFORM_API_KEY) for flow selection, reserving getAuthenticatedEmail() for the display label.

Minor

  • M3.isAuthenticated() is called twice on the authenticated + keyless-capable path (index.ts:90,94); resolve once and pass the boolean.
  • M4.packages/cli-core/src/commands/init/README.md:48-52 still documents the removed "Continue with temporary keys" prompt. Per .claude/rules/commands.md, the README must match behavior.
  • M5. New test "--starter without -y runs bootstrap interactively..." (index.test.ts:424-436) fully mocks promptAndBootstrap and never exercises the real guard at bootstrap.ts:173-177 that was the actual bug. Add a direct test for the guard.

Missed on first pass (codex caught)

  • Second README inconsistency.init/README.md:86 says non-keyless frameworks "always force authentication even with --yes", but index.ts:93-99 skips auth for unauthenticated users when -y is set.

Nits

  • isAuthenticated docstring (heuristics.ts:139-143) still references prompts that no longer exist.
  • BootstrapOverrides.implicitBootstrap doc (bootstrap.ts:135-140) doesn't note that skipConfirm supersedes it.
  • handleStarter (index.ts:153-163) dropping skipConfirm: true changes interactive --starter from previewPlan() to previewAndConfirm(). The changeset mentions --starter is "fully interactive", so this looks intentional; worth a bullet for reviewer transparency.

Positives

implicitBootstrap cleanly expresses "user opted into bootstrapping but did not opt out of prompts", better than overloading skipConfirm. Test additions cover the authentication x keyless x bootstrap/existing matrix well. isAuthenticated correctly short-circuits on CLERK_PLATFORM_API_KEY before network calls.

Skip the askSkipAuth prompt and keyless flow when the user is already
authenticated, and suppress the "keyless not supported" message outside
of the bootstrap flow.
`handleStarter` forced `skipConfirm: true` when calling the bootstrap
flow, which conflated "user already opted into bootstrapping" with
"non-interactive mode" inside `promptAndBootstrap` and caused
`clerk init --starter` (without `-y`) to fail the framework guard with:
"Non-interactive mode requires --framework for new projects".
Add a separate `implicitBootstrap` override that only skips the initial
"create a new one?" confirm, leaving `skipConfirm` to mean non-interactive.
Previously, resolveKeylessMode and the skipAuth check only consulted
getAuthenticatedEmail (OAuth). When CLERK_PLATFORM_API_KEY was set
(as in CI E2E), the user was treated as unauthenticated, so
`clerk init --yes` on a keyless-supporting framework fell through
to keyless mode instead of pulling the real keys — breaking every
E2E fixture.
Introduce an isAuthenticated helper that returns true for either
auth mechanism, and use it in both branches.
Previously, an unauthenticated user on a keyless-capable framework
was asked "Skip authentication for now?" with a default of yes.
The prompt added a step without meaningful choice in practice —
the unauthenticated path can't pull keys regardless, and the
keyless info message already points users to `clerk auth login`
for later.
Now the flow just follows auth state: signed in → authenticated
flow + env pull; not signed in → keyless. Removes `askSkipAuth`
entirely.
- Restore the `if (!bootstrap) return false` guard in `resolveKeylessMode`
so an unauthenticated re-run in an existing project falls through to the
authenticated flow (login + link + env pull) instead of silently going
keyless and skipping `env pull`.
- Make `isAuthenticated()` a pure credential-presence check (stored OAuth
token or `CLERK_PLATFORM_API_KEY`) so expired tokens, 5xx responses, or
an offline laptop no longer demote the user into keyless/skip-auth
without a prompt.
- Resolve auth once in `init()` and pass the boolean down.
- Add a direct `promptAndBootstrap` test that exercises the real
"non-interactive mode requires --framework" guard (previous coverage
fully mocked the boundary being tested).
- Update `init/README.md` and the changeset to describe the new behavior
and drop stale references to the removed "Skip authentication" prompt.
@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from ac7b139 to ea3b111CompareApril 20, 2026 15:04
@rafa-thayto
rafa-thayto merged commit 67a02d9 into mainApr 20, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/more-init-fixes branch April 20, 2026 15:08
@github-actionsgithub-actionsBot mentioned this pull request Apr 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(init): simplify auth/keyless flow and fix --starter - #175

Merged
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes
Apr 20, 2026
Merged

fix(init): simplify auth/keyless flow and fix --starter#175
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Four clerk init fixes on this branch:

  • Skip askSkipAuth and keyless flow when signed in. If the user has an OAuth token, clerk init goes straight to the authenticated flow (auth + link + env pull).
  • Count CLERK_PLATFORM_API_KEY as authenticated. Non-interactive / CI runs with an API key no longer fall through to keyless mode. A new isAuthenticated() helper covers both OAuth tokens and the env var.
  • Remove the "Skip authentication for now?" prompt. The prompt added a step without meaningful choice — the unauthenticated path can't pull keys either way. Now the flow just follows auth state: signed in → authenticated flow + env pull; not signed in → keyless automatically (the keyless info message already points users to clerk auth login for later).
  • Keep clerk init --starter interactive without --framework. Running --starter without -y no longer errors with "Non-interactive mode requires --framework" — it only implies "yes, bootstrap," not "skip every prompt."

Also suppresses the "keyless not supported" framework message outside the bootstrap flow.

Test plan

  • bun run test — init suite covers the permutations (auth vs. not, API key vs. OAuth, bootstrap vs. existing repo, --starter with and without -y)
  • bun run lint / bun run typecheck
  • CI E2E suite passes (was failing on Astro / Next / TanStack / React Router before the API-key fix)
  • Manual: clerk init on a keyless framework while signed in → authenticated flow, no keyless prompt
  • Manual: clerk init on a keyless framework while signed out → keyless flow, no prompt
  • Manual: clerk init in an existing repo with keyless framework while signed in → no keyless prompt, env pulled
  • Manual: clerk init --starter (no -y) → interactive framework picker instead of an error

@changeset-bot

changeset-botBot commented Apr 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea3b111

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rafa-thayto has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fcab2f5-4b16-4323-8e0f-2e9d72b71f17

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70430 and ea3b111.

📒 Files selected for processing (7)
  • .changeset/more-init-fixes.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/bootstrap.test.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/heuristics.ts
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
📝 Walkthrough

Walkthrough

The init command flow was changed to check authentication explicitly before skipping prompts. resolveKeylessMode now returns false when already authenticated and only enables keyless for unauthenticated, keyless-capable frameworks; it avoids logging “keyless not yet supported” unless bootstrap is requested. handleStarter and bootstrap calls now pass an implicitBootstrap flag instead of skipConfirm to bypass only the initial “create a new one?” confirmation. The exported askSkipAuth helper was removed and promptAndBootstrap gained an implicitBootstrap option. A new heuristics.isAuthenticated() helper was added (checks CLERK_PLATFORM_API_KEY or stored auth). Tests were updated to use isAuthenticated and to expand keyless/auth coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description check✅ PassedThe pull request description clearly and comprehensively explains the changes, including four specific fixes to the clerk init command with concrete examples and a detailed test plan.
Title check✅ PassedThe title accurately summarizes the main changes: fixing authentication/keyless flow logic and resolving the --starter flag issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417120320
PackageVersion
clerk0.0.3-snapshot.v20260417120320

Published from 5cf3c19

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 5cf3c19 to 7d45a72CompareApril 17, 2026 13:21
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417132423
PackageVersion
clerk0.0.3-snapshot.v20260417132423

Published from 7d45a72

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417160442
PackageVersion
clerk0.0.3-snapshot.v20260417160442

Published from 9a70430

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 9a70430 to 1682ce6CompareApril 17, 2026 16:54
@rafa-thaytorafa-thayto changed the title fix(init): check auth before keyless/skip-auth promptsfix(init): simplify auth/keyless flow and fix --starterApr 17, 2026

@wyattjohwyattjoh left a comment

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.

Code Review — PR #175

Reviewed with Opus + Codex second-opinion validation.

Major

M1. Removing the bootstrap != null guard broadens keyless auto-select to existing projects(codex partial)
packages/cli-core/src/commands/init/index.ts:215-225 now returns keyless = true for any unauthenticated user on a keyless-capable framework, not only during bootstrap. Downstream effects:

  1. authenticateAndLink is skipped when users re-run clerk init signed out on an existing Clerk project.
  2. ctx.keyless = true at frameworks/helpers.ts:204-221,461-467 toggles permissive clerkMiddleware() (no route protection). Idempotency via hasClerkImport should prevent overwrites, but a scaffold-idempotency test for existingClerk: true + keyless: true would make the invariant load-bearing rather than incidental.
  3. env pull is skipped for the keyless branch.

The changeset does note keyless auto-select behavior, but the existing-project qualifier is not spelled out. Consider either restoring the if (!bootstrap) return false guard, or explicitly documenting the existing-project case in README and changeset.

M2. isAuthenticated() conflates "no credentials" with "network/API failure"(codex partial)
packages/cli-core/src/commands/init/heuristics.ts:139-147 delegates to getAuthenticatedEmail() which catches all errors and returns null. Expired tokens, Clerk 5xx, or offline laptops all silently demote the user into keyless (for keyless-capable frameworks) or skip-auth. Previously askSkipAuth gave the user a decision point. Consider a lighter credential-presence check (getToken() + CLERK_PLATFORM_API_KEY) for flow selection, reserving getAuthenticatedEmail() for the display label.

Minor

  • M3.isAuthenticated() is called twice on the authenticated + keyless-capable path (index.ts:90,94); resolve once and pass the boolean.
  • M4.packages/cli-core/src/commands/init/README.md:48-52 still documents the removed "Continue with temporary keys" prompt. Per .claude/rules/commands.md, the README must match behavior.
  • M5. New test "--starter without -y runs bootstrap interactively..." (index.test.ts:424-436) fully mocks promptAndBootstrap and never exercises the real guard at bootstrap.ts:173-177 that was the actual bug. Add a direct test for the guard.

Missed on first pass (codex caught)

  • Second README inconsistency.init/README.md:86 says non-keyless frameworks "always force authentication even with --yes", but index.ts:93-99 skips auth for unauthenticated users when -y is set.

Nits

  • isAuthenticated docstring (heuristics.ts:139-143) still references prompts that no longer exist.
  • BootstrapOverrides.implicitBootstrap doc (bootstrap.ts:135-140) doesn't note that skipConfirm supersedes it.
  • handleStarter (index.ts:153-163) dropping skipConfirm: true changes interactive --starter from previewPlan() to previewAndConfirm(). The changeset mentions --starter is "fully interactive", so this looks intentional; worth a bullet for reviewer transparency.

Positives

implicitBootstrap cleanly expresses "user opted into bootstrapping but did not opt out of prompts", better than overloading skipConfirm. Test additions cover the authentication x keyless x bootstrap/existing matrix well. isAuthenticated correctly short-circuits on CLERK_PLATFORM_API_KEY before network calls.

Skip the askSkipAuth prompt and keyless flow when the user is already
authenticated, and suppress the "keyless not supported" message outside
of the bootstrap flow.
`handleStarter` forced `skipConfirm: true` when calling the bootstrap
flow, which conflated "user already opted into bootstrapping" with
"non-interactive mode" inside `promptAndBootstrap` and caused
`clerk init --starter` (without `-y`) to fail the framework guard with:
"Non-interactive mode requires --framework for new projects".
Add a separate `implicitBootstrap` override that only skips the initial
"create a new one?" confirm, leaving `skipConfirm` to mean non-interactive.
Previously, resolveKeylessMode and the skipAuth check only consulted
getAuthenticatedEmail (OAuth). When CLERK_PLATFORM_API_KEY was set
(as in CI E2E), the user was treated as unauthenticated, so
`clerk init --yes` on a keyless-supporting framework fell through
to keyless mode instead of pulling the real keys — breaking every
E2E fixture.
Introduce an isAuthenticated helper that returns true for either
auth mechanism, and use it in both branches.
Previously, an unauthenticated user on a keyless-capable framework
was asked "Skip authentication for now?" with a default of yes.
The prompt added a step without meaningful choice in practice —
the unauthenticated path can't pull keys regardless, and the
keyless info message already points users to `clerk auth login`
for later.
Now the flow just follows auth state: signed in → authenticated
flow + env pull; not signed in → keyless. Removes `askSkipAuth`
entirely.
- Restore the `if (!bootstrap) return false` guard in `resolveKeylessMode`
so an unauthenticated re-run in an existing project falls through to the
authenticated flow (login + link + env pull) instead of silently going
keyless and skipping `env pull`.
- Make `isAuthenticated()` a pure credential-presence check (stored OAuth
token or `CLERK_PLATFORM_API_KEY`) so expired tokens, 5xx responses, or
an offline laptop no longer demote the user into keyless/skip-auth
without a prompt.
- Resolve auth once in `init()` and pass the boolean down.
- Add a direct `promptAndBootstrap` test that exercises the real
"non-interactive mode requires --framework" guard (previous coverage
fully mocked the boundary being tested).
- Update `init/README.md` and the changeset to describe the new behavior
and drop stale references to the removed "Skip authentication" prompt.
@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from ac7b139 to ea3b111CompareApril 20, 2026 15:04
@rafa-thayto
rafa-thayto merged commit 67a02d9 into mainApr 20, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/more-init-fixes branch April 20, 2026 15:08
@github-actionsgithub-actionsBot mentioned this pull request Apr 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(init): simplify auth/keyless flow and fix --starter - #175

Merged
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes
Apr 20, 2026
Merged

fix(init): simplify auth/keyless flow and fix --starter#175
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Four clerk init fixes on this branch:

  • Skip askSkipAuth and keyless flow when signed in. If the user has an OAuth token, clerk init goes straight to the authenticated flow (auth + link + env pull).
  • Count CLERK_PLATFORM_API_KEY as authenticated. Non-interactive / CI runs with an API key no longer fall through to keyless mode. A new isAuthenticated() helper covers both OAuth tokens and the env var.
  • Remove the "Skip authentication for now?" prompt. The prompt added a step without meaningful choice — the unauthenticated path can't pull keys either way. Now the flow just follows auth state: signed in → authenticated flow + env pull; not signed in → keyless automatically (the keyless info message already points users to clerk auth login for later).
  • Keep clerk init --starter interactive without --framework. Running --starter without -y no longer errors with "Non-interactive mode requires --framework" — it only implies "yes, bootstrap," not "skip every prompt."

Also suppresses the "keyless not supported" framework message outside the bootstrap flow.

Test plan

  • bun run test — init suite covers the permutations (auth vs. not, API key vs. OAuth, bootstrap vs. existing repo, --starter with and without -y)
  • bun run lint / bun run typecheck
  • CI E2E suite passes (was failing on Astro / Next / TanStack / React Router before the API-key fix)
  • Manual: clerk init on a keyless framework while signed in → authenticated flow, no keyless prompt
  • Manual: clerk init on a keyless framework while signed out → keyless flow, no prompt
  • Manual: clerk init in an existing repo with keyless framework while signed in → no keyless prompt, env pulled
  • Manual: clerk init --starter (no -y) → interactive framework picker instead of an error

@changeset-bot

changeset-botBot commented Apr 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea3b111

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rafa-thayto has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fcab2f5-4b16-4323-8e0f-2e9d72b71f17

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70430 and ea3b111.

📒 Files selected for processing (7)
  • .changeset/more-init-fixes.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/bootstrap.test.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/heuristics.ts
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
📝 Walkthrough

Walkthrough

The init command flow was changed to check authentication explicitly before skipping prompts. resolveKeylessMode now returns false when already authenticated and only enables keyless for unauthenticated, keyless-capable frameworks; it avoids logging “keyless not yet supported” unless bootstrap is requested. handleStarter and bootstrap calls now pass an implicitBootstrap flag instead of skipConfirm to bypass only the initial “create a new one?” confirmation. The exported askSkipAuth helper was removed and promptAndBootstrap gained an implicitBootstrap option. A new heuristics.isAuthenticated() helper was added (checks CLERK_PLATFORM_API_KEY or stored auth). Tests were updated to use isAuthenticated and to expand keyless/auth coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description check✅ PassedThe pull request description clearly and comprehensively explains the changes, including four specific fixes to the clerk init command with concrete examples and a detailed test plan.
Title check✅ PassedThe title accurately summarizes the main changes: fixing authentication/keyless flow logic and resolving the --starter flag issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417120320
PackageVersion
clerk0.0.3-snapshot.v20260417120320

Published from 5cf3c19

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 5cf3c19 to 7d45a72CompareApril 17, 2026 13:21
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417132423
PackageVersion
clerk0.0.3-snapshot.v20260417132423

Published from 7d45a72

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417160442
PackageVersion
clerk0.0.3-snapshot.v20260417160442

Published from 9a70430

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 9a70430 to 1682ce6CompareApril 17, 2026 16:54
@rafa-thaytorafa-thayto changed the title fix(init): check auth before keyless/skip-auth promptsfix(init): simplify auth/keyless flow and fix --starterApr 17, 2026

@wyattjohwyattjoh left a comment

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.

Code Review — PR #175

Reviewed with Opus + Codex second-opinion validation.

Major

M1. Removing the bootstrap != null guard broadens keyless auto-select to existing projects(codex partial)
packages/cli-core/src/commands/init/index.ts:215-225 now returns keyless = true for any unauthenticated user on a keyless-capable framework, not only during bootstrap. Downstream effects:

  1. authenticateAndLink is skipped when users re-run clerk init signed out on an existing Clerk project.
  2. ctx.keyless = true at frameworks/helpers.ts:204-221,461-467 toggles permissive clerkMiddleware() (no route protection). Idempotency via hasClerkImport should prevent overwrites, but a scaffold-idempotency test for existingClerk: true + keyless: true would make the invariant load-bearing rather than incidental.
  3. env pull is skipped for the keyless branch.

The changeset does note keyless auto-select behavior, but the existing-project qualifier is not spelled out. Consider either restoring the if (!bootstrap) return false guard, or explicitly documenting the existing-project case in README and changeset.

M2. isAuthenticated() conflates "no credentials" with "network/API failure"(codex partial)
packages/cli-core/src/commands/init/heuristics.ts:139-147 delegates to getAuthenticatedEmail() which catches all errors and returns null. Expired tokens, Clerk 5xx, or offline laptops all silently demote the user into keyless (for keyless-capable frameworks) or skip-auth. Previously askSkipAuth gave the user a decision point. Consider a lighter credential-presence check (getToken() + CLERK_PLATFORM_API_KEY) for flow selection, reserving getAuthenticatedEmail() for the display label.

Minor

  • M3.isAuthenticated() is called twice on the authenticated + keyless-capable path (index.ts:90,94); resolve once and pass the boolean.
  • M4.packages/cli-core/src/commands/init/README.md:48-52 still documents the removed "Continue with temporary keys" prompt. Per .claude/rules/commands.md, the README must match behavior.
  • M5. New test "--starter without -y runs bootstrap interactively..." (index.test.ts:424-436) fully mocks promptAndBootstrap and never exercises the real guard at bootstrap.ts:173-177 that was the actual bug. Add a direct test for the guard.

Missed on first pass (codex caught)

  • Second README inconsistency.init/README.md:86 says non-keyless frameworks "always force authentication even with --yes", but index.ts:93-99 skips auth for unauthenticated users when -y is set.

Nits

  • isAuthenticated docstring (heuristics.ts:139-143) still references prompts that no longer exist.
  • BootstrapOverrides.implicitBootstrap doc (bootstrap.ts:135-140) doesn't note that skipConfirm supersedes it.
  • handleStarter (index.ts:153-163) dropping skipConfirm: true changes interactive --starter from previewPlan() to previewAndConfirm(). The changeset mentions --starter is "fully interactive", so this looks intentional; worth a bullet for reviewer transparency.

Positives

implicitBootstrap cleanly expresses "user opted into bootstrapping but did not opt out of prompts", better than overloading skipConfirm. Test additions cover the authentication x keyless x bootstrap/existing matrix well. isAuthenticated correctly short-circuits on CLERK_PLATFORM_API_KEY before network calls.

Skip the askSkipAuth prompt and keyless flow when the user is already
authenticated, and suppress the "keyless not supported" message outside
of the bootstrap flow.
`handleStarter` forced `skipConfirm: true` when calling the bootstrap
flow, which conflated "user already opted into bootstrapping" with
"non-interactive mode" inside `promptAndBootstrap` and caused
`clerk init --starter` (without `-y`) to fail the framework guard with:
"Non-interactive mode requires --framework for new projects".
Add a separate `implicitBootstrap` override that only skips the initial
"create a new one?" confirm, leaving `skipConfirm` to mean non-interactive.
Previously, resolveKeylessMode and the skipAuth check only consulted
getAuthenticatedEmail (OAuth). When CLERK_PLATFORM_API_KEY was set
(as in CI E2E), the user was treated as unauthenticated, so
`clerk init --yes` on a keyless-supporting framework fell through
to keyless mode instead of pulling the real keys — breaking every
E2E fixture.
Introduce an isAuthenticated helper that returns true for either
auth mechanism, and use it in both branches.
Previously, an unauthenticated user on a keyless-capable framework
was asked "Skip authentication for now?" with a default of yes.
The prompt added a step without meaningful choice in practice —
the unauthenticated path can't pull keys regardless, and the
keyless info message already points users to `clerk auth login`
for later.
Now the flow just follows auth state: signed in → authenticated
flow + env pull; not signed in → keyless. Removes `askSkipAuth`
entirely.
- Restore the `if (!bootstrap) return false` guard in `resolveKeylessMode`
so an unauthenticated re-run in an existing project falls through to the
authenticated flow (login + link + env pull) instead of silently going
keyless and skipping `env pull`.
- Make `isAuthenticated()` a pure credential-presence check (stored OAuth
token or `CLERK_PLATFORM_API_KEY`) so expired tokens, 5xx responses, or
an offline laptop no longer demote the user into keyless/skip-auth
without a prompt.
- Resolve auth once in `init()` and pass the boolean down.
- Add a direct `promptAndBootstrap` test that exercises the real
"non-interactive mode requires --framework" guard (previous coverage
fully mocked the boundary being tested).
- Update `init/README.md` and the changeset to describe the new behavior
and drop stale references to the removed "Skip authentication" prompt.
@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from ac7b139 to ea3b111CompareApril 20, 2026 15:04
@rafa-thayto
rafa-thayto merged commit 67a02d9 into mainApr 20, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/more-init-fixes branch April 20, 2026 15:08
@github-actionsgithub-actionsBot mentioned this pull request Apr 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(init): simplify auth/keyless flow and fix --starter - #175

Merged
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes
Apr 20, 2026
Merged

fix(init): simplify auth/keyless flow and fix --starter#175
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Four clerk init fixes on this branch:

  • Skip askSkipAuth and keyless flow when signed in. If the user has an OAuth token, clerk init goes straight to the authenticated flow (auth + link + env pull).
  • Count CLERK_PLATFORM_API_KEY as authenticated. Non-interactive / CI runs with an API key no longer fall through to keyless mode. A new isAuthenticated() helper covers both OAuth tokens and the env var.
  • Remove the "Skip authentication for now?" prompt. The prompt added a step without meaningful choice — the unauthenticated path can't pull keys either way. Now the flow just follows auth state: signed in → authenticated flow + env pull; not signed in → keyless automatically (the keyless info message already points users to clerk auth login for later).
  • Keep clerk init --starter interactive without --framework. Running --starter without -y no longer errors with "Non-interactive mode requires --framework" — it only implies "yes, bootstrap," not "skip every prompt."

Also suppresses the "keyless not supported" framework message outside the bootstrap flow.

Test plan

  • bun run test — init suite covers the permutations (auth vs. not, API key vs. OAuth, bootstrap vs. existing repo, --starter with and without -y)
  • bun run lint / bun run typecheck
  • CI E2E suite passes (was failing on Astro / Next / TanStack / React Router before the API-key fix)
  • Manual: clerk init on a keyless framework while signed in → authenticated flow, no keyless prompt
  • Manual: clerk init on a keyless framework while signed out → keyless flow, no prompt
  • Manual: clerk init in an existing repo with keyless framework while signed in → no keyless prompt, env pulled
  • Manual: clerk init --starter (no -y) → interactive framework picker instead of an error

@changeset-bot

changeset-botBot commented Apr 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea3b111

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rafa-thayto has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fcab2f5-4b16-4323-8e0f-2e9d72b71f17

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70430 and ea3b111.

📒 Files selected for processing (7)
  • .changeset/more-init-fixes.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/bootstrap.test.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/heuristics.ts
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
📝 Walkthrough

Walkthrough

The init command flow was changed to check authentication explicitly before skipping prompts. resolveKeylessMode now returns false when already authenticated and only enables keyless for unauthenticated, keyless-capable frameworks; it avoids logging “keyless not yet supported” unless bootstrap is requested. handleStarter and bootstrap calls now pass an implicitBootstrap flag instead of skipConfirm to bypass only the initial “create a new one?” confirmation. The exported askSkipAuth helper was removed and promptAndBootstrap gained an implicitBootstrap option. A new heuristics.isAuthenticated() helper was added (checks CLERK_PLATFORM_API_KEY or stored auth). Tests were updated to use isAuthenticated and to expand keyless/auth coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description check✅ PassedThe pull request description clearly and comprehensively explains the changes, including four specific fixes to the clerk init command with concrete examples and a detailed test plan.
Title check✅ PassedThe title accurately summarizes the main changes: fixing authentication/keyless flow logic and resolving the --starter flag issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417120320
PackageVersion
clerk0.0.3-snapshot.v20260417120320

Published from 5cf3c19

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 5cf3c19 to 7d45a72CompareApril 17, 2026 13:21
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417132423
PackageVersion
clerk0.0.3-snapshot.v20260417132423

Published from 7d45a72

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417160442
PackageVersion
clerk0.0.3-snapshot.v20260417160442

Published from 9a70430

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 9a70430 to 1682ce6CompareApril 17, 2026 16:54
@rafa-thaytorafa-thayto changed the title fix(init): check auth before keyless/skip-auth promptsfix(init): simplify auth/keyless flow and fix --starterApr 17, 2026

@wyattjohwyattjoh left a comment

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.

Code Review — PR #175

Reviewed with Opus + Codex second-opinion validation.

Major

M1. Removing the bootstrap != null guard broadens keyless auto-select to existing projects(codex partial)
packages/cli-core/src/commands/init/index.ts:215-225 now returns keyless = true for any unauthenticated user on a keyless-capable framework, not only during bootstrap. Downstream effects:

  1. authenticateAndLink is skipped when users re-run clerk init signed out on an existing Clerk project.
  2. ctx.keyless = true at frameworks/helpers.ts:204-221,461-467 toggles permissive clerkMiddleware() (no route protection). Idempotency via hasClerkImport should prevent overwrites, but a scaffold-idempotency test for existingClerk: true + keyless: true would make the invariant load-bearing rather than incidental.
  3. env pull is skipped for the keyless branch.

The changeset does note keyless auto-select behavior, but the existing-project qualifier is not spelled out. Consider either restoring the if (!bootstrap) return false guard, or explicitly documenting the existing-project case in README and changeset.

M2. isAuthenticated() conflates "no credentials" with "network/API failure"(codex partial)
packages/cli-core/src/commands/init/heuristics.ts:139-147 delegates to getAuthenticatedEmail() which catches all errors and returns null. Expired tokens, Clerk 5xx, or offline laptops all silently demote the user into keyless (for keyless-capable frameworks) or skip-auth. Previously askSkipAuth gave the user a decision point. Consider a lighter credential-presence check (getToken() + CLERK_PLATFORM_API_KEY) for flow selection, reserving getAuthenticatedEmail() for the display label.

Minor

  • M3.isAuthenticated() is called twice on the authenticated + keyless-capable path (index.ts:90,94); resolve once and pass the boolean.
  • M4.packages/cli-core/src/commands/init/README.md:48-52 still documents the removed "Continue with temporary keys" prompt. Per .claude/rules/commands.md, the README must match behavior.
  • M5. New test "--starter without -y runs bootstrap interactively..." (index.test.ts:424-436) fully mocks promptAndBootstrap and never exercises the real guard at bootstrap.ts:173-177 that was the actual bug. Add a direct test for the guard.

Missed on first pass (codex caught)

  • Second README inconsistency.init/README.md:86 says non-keyless frameworks "always force authentication even with --yes", but index.ts:93-99 skips auth for unauthenticated users when -y is set.

Nits

  • isAuthenticated docstring (heuristics.ts:139-143) still references prompts that no longer exist.
  • BootstrapOverrides.implicitBootstrap doc (bootstrap.ts:135-140) doesn't note that skipConfirm supersedes it.
  • handleStarter (index.ts:153-163) dropping skipConfirm: true changes interactive --starter from previewPlan() to previewAndConfirm(). The changeset mentions --starter is "fully interactive", so this looks intentional; worth a bullet for reviewer transparency.

Positives

implicitBootstrap cleanly expresses "user opted into bootstrapping but did not opt out of prompts", better than overloading skipConfirm. Test additions cover the authentication x keyless x bootstrap/existing matrix well. isAuthenticated correctly short-circuits on CLERK_PLATFORM_API_KEY before network calls.

Skip the askSkipAuth prompt and keyless flow when the user is already
authenticated, and suppress the "keyless not supported" message outside
of the bootstrap flow.
`handleStarter` forced `skipConfirm: true` when calling the bootstrap
flow, which conflated "user already opted into bootstrapping" with
"non-interactive mode" inside `promptAndBootstrap` and caused
`clerk init --starter` (without `-y`) to fail the framework guard with:
"Non-interactive mode requires --framework for new projects".
Add a separate `implicitBootstrap` override that only skips the initial
"create a new one?" confirm, leaving `skipConfirm` to mean non-interactive.
Previously, resolveKeylessMode and the skipAuth check only consulted
getAuthenticatedEmail (OAuth). When CLERK_PLATFORM_API_KEY was set
(as in CI E2E), the user was treated as unauthenticated, so
`clerk init --yes` on a keyless-supporting framework fell through
to keyless mode instead of pulling the real keys — breaking every
E2E fixture.
Introduce an isAuthenticated helper that returns true for either
auth mechanism, and use it in both branches.
Previously, an unauthenticated user on a keyless-capable framework
was asked "Skip authentication for now?" with a default of yes.
The prompt added a step without meaningful choice in practice —
the unauthenticated path can't pull keys regardless, and the
keyless info message already points users to `clerk auth login`
for later.
Now the flow just follows auth state: signed in → authenticated
flow + env pull; not signed in → keyless. Removes `askSkipAuth`
entirely.
- Restore the `if (!bootstrap) return false` guard in `resolveKeylessMode`
so an unauthenticated re-run in an existing project falls through to the
authenticated flow (login + link + env pull) instead of silently going
keyless and skipping `env pull`.
- Make `isAuthenticated()` a pure credential-presence check (stored OAuth
token or `CLERK_PLATFORM_API_KEY`) so expired tokens, 5xx responses, or
an offline laptop no longer demote the user into keyless/skip-auth
without a prompt.
- Resolve auth once in `init()` and pass the boolean down.
- Add a direct `promptAndBootstrap` test that exercises the real
"non-interactive mode requires --framework" guard (previous coverage
fully mocked the boundary being tested).
- Update `init/README.md` and the changeset to describe the new behavior
and drop stale references to the removed "Skip authentication" prompt.
@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from ac7b139 to ea3b111CompareApril 20, 2026 15:04
@rafa-thayto
rafa-thayto merged commit 67a02d9 into mainApr 20, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/more-init-fixes branch April 20, 2026 15:08
@github-actionsgithub-actionsBot mentioned this pull request Apr 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(init): simplify auth/keyless flow and fix --starter - #175

Merged
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes
Apr 20, 2026
Merged

fix(init): simplify auth/keyless flow and fix --starter#175
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Four clerk init fixes on this branch:

  • Skip askSkipAuth and keyless flow when signed in. If the user has an OAuth token, clerk init goes straight to the authenticated flow (auth + link + env pull).
  • Count CLERK_PLATFORM_API_KEY as authenticated. Non-interactive / CI runs with an API key no longer fall through to keyless mode. A new isAuthenticated() helper covers both OAuth tokens and the env var.
  • Remove the "Skip authentication for now?" prompt. The prompt added a step without meaningful choice — the unauthenticated path can't pull keys either way. Now the flow just follows auth state: signed in → authenticated flow + env pull; not signed in → keyless automatically (the keyless info message already points users to clerk auth login for later).
  • Keep clerk init --starter interactive without --framework. Running --starter without -y no longer errors with "Non-interactive mode requires --framework" — it only implies "yes, bootstrap," not "skip every prompt."

Also suppresses the "keyless not supported" framework message outside the bootstrap flow.

Test plan

  • bun run test — init suite covers the permutations (auth vs. not, API key vs. OAuth, bootstrap vs. existing repo, --starter with and without -y)
  • bun run lint / bun run typecheck
  • CI E2E suite passes (was failing on Astro / Next / TanStack / React Router before the API-key fix)
  • Manual: clerk init on a keyless framework while signed in → authenticated flow, no keyless prompt
  • Manual: clerk init on a keyless framework while signed out → keyless flow, no prompt
  • Manual: clerk init in an existing repo with keyless framework while signed in → no keyless prompt, env pulled
  • Manual: clerk init --starter (no -y) → interactive framework picker instead of an error

@changeset-bot

changeset-botBot commented Apr 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea3b111

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rafa-thayto has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fcab2f5-4b16-4323-8e0f-2e9d72b71f17

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70430 and ea3b111.

📒 Files selected for processing (7)
  • .changeset/more-init-fixes.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/bootstrap.test.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/heuristics.ts
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
📝 Walkthrough

Walkthrough

The init command flow was changed to check authentication explicitly before skipping prompts. resolveKeylessMode now returns false when already authenticated and only enables keyless for unauthenticated, keyless-capable frameworks; it avoids logging “keyless not yet supported” unless bootstrap is requested. handleStarter and bootstrap calls now pass an implicitBootstrap flag instead of skipConfirm to bypass only the initial “create a new one?” confirmation. The exported askSkipAuth helper was removed and promptAndBootstrap gained an implicitBootstrap option. A new heuristics.isAuthenticated() helper was added (checks CLERK_PLATFORM_API_KEY or stored auth). Tests were updated to use isAuthenticated and to expand keyless/auth coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description check✅ PassedThe pull request description clearly and comprehensively explains the changes, including four specific fixes to the clerk init command with concrete examples and a detailed test plan.
Title check✅ PassedThe title accurately summarizes the main changes: fixing authentication/keyless flow logic and resolving the --starter flag issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417120320
PackageVersion
clerk0.0.3-snapshot.v20260417120320

Published from 5cf3c19

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 5cf3c19 to 7d45a72CompareApril 17, 2026 13:21
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417132423
PackageVersion
clerk0.0.3-snapshot.v20260417132423

Published from 7d45a72

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417160442
PackageVersion
clerk0.0.3-snapshot.v20260417160442

Published from 9a70430

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 9a70430 to 1682ce6CompareApril 17, 2026 16:54
@rafa-thaytorafa-thayto changed the title fix(init): check auth before keyless/skip-auth promptsfix(init): simplify auth/keyless flow and fix --starterApr 17, 2026

@wyattjohwyattjoh left a comment

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.

Code Review — PR #175

Reviewed with Opus + Codex second-opinion validation.

Major

M1. Removing the bootstrap != null guard broadens keyless auto-select to existing projects(codex partial)
packages/cli-core/src/commands/init/index.ts:215-225 now returns keyless = true for any unauthenticated user on a keyless-capable framework, not only during bootstrap. Downstream effects:

  1. authenticateAndLink is skipped when users re-run clerk init signed out on an existing Clerk project.
  2. ctx.keyless = true at frameworks/helpers.ts:204-221,461-467 toggles permissive clerkMiddleware() (no route protection). Idempotency via hasClerkImport should prevent overwrites, but a scaffold-idempotency test for existingClerk: true + keyless: true would make the invariant load-bearing rather than incidental.
  3. env pull is skipped for the keyless branch.

The changeset does note keyless auto-select behavior, but the existing-project qualifier is not spelled out. Consider either restoring the if (!bootstrap) return false guard, or explicitly documenting the existing-project case in README and changeset.

M2. isAuthenticated() conflates "no credentials" with "network/API failure"(codex partial)
packages/cli-core/src/commands/init/heuristics.ts:139-147 delegates to getAuthenticatedEmail() which catches all errors and returns null. Expired tokens, Clerk 5xx, or offline laptops all silently demote the user into keyless (for keyless-capable frameworks) or skip-auth. Previously askSkipAuth gave the user a decision point. Consider a lighter credential-presence check (getToken() + CLERK_PLATFORM_API_KEY) for flow selection, reserving getAuthenticatedEmail() for the display label.

Minor

  • M3.isAuthenticated() is called twice on the authenticated + keyless-capable path (index.ts:90,94); resolve once and pass the boolean.
  • M4.packages/cli-core/src/commands/init/README.md:48-52 still documents the removed "Continue with temporary keys" prompt. Per .claude/rules/commands.md, the README must match behavior.
  • M5. New test "--starter without -y runs bootstrap interactively..." (index.test.ts:424-436) fully mocks promptAndBootstrap and never exercises the real guard at bootstrap.ts:173-177 that was the actual bug. Add a direct test for the guard.

Missed on first pass (codex caught)

  • Second README inconsistency.init/README.md:86 says non-keyless frameworks "always force authentication even with --yes", but index.ts:93-99 skips auth for unauthenticated users when -y is set.

Nits

  • isAuthenticated docstring (heuristics.ts:139-143) still references prompts that no longer exist.
  • BootstrapOverrides.implicitBootstrap doc (bootstrap.ts:135-140) doesn't note that skipConfirm supersedes it.
  • handleStarter (index.ts:153-163) dropping skipConfirm: true changes interactive --starter from previewPlan() to previewAndConfirm(). The changeset mentions --starter is "fully interactive", so this looks intentional; worth a bullet for reviewer transparency.

Positives

implicitBootstrap cleanly expresses "user opted into bootstrapping but did not opt out of prompts", better than overloading skipConfirm. Test additions cover the authentication x keyless x bootstrap/existing matrix well. isAuthenticated correctly short-circuits on CLERK_PLATFORM_API_KEY before network calls.

Skip the askSkipAuth prompt and keyless flow when the user is already
authenticated, and suppress the "keyless not supported" message outside
of the bootstrap flow.
`handleStarter` forced `skipConfirm: true` when calling the bootstrap
flow, which conflated "user already opted into bootstrapping" with
"non-interactive mode" inside `promptAndBootstrap` and caused
`clerk init --starter` (without `-y`) to fail the framework guard with:
"Non-interactive mode requires --framework for new projects".
Add a separate `implicitBootstrap` override that only skips the initial
"create a new one?" confirm, leaving `skipConfirm` to mean non-interactive.
Previously, resolveKeylessMode and the skipAuth check only consulted
getAuthenticatedEmail (OAuth). When CLERK_PLATFORM_API_KEY was set
(as in CI E2E), the user was treated as unauthenticated, so
`clerk init --yes` on a keyless-supporting framework fell through
to keyless mode instead of pulling the real keys — breaking every
E2E fixture.
Introduce an isAuthenticated helper that returns true for either
auth mechanism, and use it in both branches.
Previously, an unauthenticated user on a keyless-capable framework
was asked "Skip authentication for now?" with a default of yes.
The prompt added a step without meaningful choice in practice —
the unauthenticated path can't pull keys regardless, and the
keyless info message already points users to `clerk auth login`
for later.
Now the flow just follows auth state: signed in → authenticated
flow + env pull; not signed in → keyless. Removes `askSkipAuth`
entirely.
- Restore the `if (!bootstrap) return false` guard in `resolveKeylessMode`
so an unauthenticated re-run in an existing project falls through to the
authenticated flow (login + link + env pull) instead of silently going
keyless and skipping `env pull`.
- Make `isAuthenticated()` a pure credential-presence check (stored OAuth
token or `CLERK_PLATFORM_API_KEY`) so expired tokens, 5xx responses, or
an offline laptop no longer demote the user into keyless/skip-auth
without a prompt.
- Resolve auth once in `init()` and pass the boolean down.
- Add a direct `promptAndBootstrap` test that exercises the real
"non-interactive mode requires --framework" guard (previous coverage
fully mocked the boundary being tested).
- Update `init/README.md` and the changeset to describe the new behavior
and drop stale references to the removed "Skip authentication" prompt.
@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from ac7b139 to ea3b111CompareApril 20, 2026 15:04
@rafa-thayto
rafa-thayto merged commit 67a02d9 into mainApr 20, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/more-init-fixes branch April 20, 2026 15:08
@github-actionsgithub-actionsBot mentioned this pull request Apr 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(init): simplify auth/keyless flow and fix --starter - #175

Merged
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes
Apr 20, 2026
Merged

fix(init): simplify auth/keyless flow and fix --starter#175
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Four clerk init fixes on this branch:

  • Skip askSkipAuth and keyless flow when signed in. If the user has an OAuth token, clerk init goes straight to the authenticated flow (auth + link + env pull).
  • Count CLERK_PLATFORM_API_KEY as authenticated. Non-interactive / CI runs with an API key no longer fall through to keyless mode. A new isAuthenticated() helper covers both OAuth tokens and the env var.
  • Remove the "Skip authentication for now?" prompt. The prompt added a step without meaningful choice — the unauthenticated path can't pull keys either way. Now the flow just follows auth state: signed in → authenticated flow + env pull; not signed in → keyless automatically (the keyless info message already points users to clerk auth login for later).
  • Keep clerk init --starter interactive without --framework. Running --starter without -y no longer errors with "Non-interactive mode requires --framework" — it only implies "yes, bootstrap," not "skip every prompt."

Also suppresses the "keyless not supported" framework message outside the bootstrap flow.

Test plan

  • bun run test — init suite covers the permutations (auth vs. not, API key vs. OAuth, bootstrap vs. existing repo, --starter with and without -y)
  • bun run lint / bun run typecheck
  • CI E2E suite passes (was failing on Astro / Next / TanStack / React Router before the API-key fix)
  • Manual: clerk init on a keyless framework while signed in → authenticated flow, no keyless prompt
  • Manual: clerk init on a keyless framework while signed out → keyless flow, no prompt
  • Manual: clerk init in an existing repo with keyless framework while signed in → no keyless prompt, env pulled
  • Manual: clerk init --starter (no -y) → interactive framework picker instead of an error

@changeset-bot

changeset-botBot commented Apr 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea3b111

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rafa-thayto has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fcab2f5-4b16-4323-8e0f-2e9d72b71f17

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70430 and ea3b111.

📒 Files selected for processing (7)
  • .changeset/more-init-fixes.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/bootstrap.test.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/heuristics.ts
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
📝 Walkthrough

Walkthrough

The init command flow was changed to check authentication explicitly before skipping prompts. resolveKeylessMode now returns false when already authenticated and only enables keyless for unauthenticated, keyless-capable frameworks; it avoids logging “keyless not yet supported” unless bootstrap is requested. handleStarter and bootstrap calls now pass an implicitBootstrap flag instead of skipConfirm to bypass only the initial “create a new one?” confirmation. The exported askSkipAuth helper was removed and promptAndBootstrap gained an implicitBootstrap option. A new heuristics.isAuthenticated() helper was added (checks CLERK_PLATFORM_API_KEY or stored auth). Tests were updated to use isAuthenticated and to expand keyless/auth coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description check✅ PassedThe pull request description clearly and comprehensively explains the changes, including four specific fixes to the clerk init command with concrete examples and a detailed test plan.
Title check✅ PassedThe title accurately summarizes the main changes: fixing authentication/keyless flow logic and resolving the --starter flag issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417120320
PackageVersion
clerk0.0.3-snapshot.v20260417120320

Published from 5cf3c19

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 5cf3c19 to 7d45a72CompareApril 17, 2026 13:21
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417132423
PackageVersion
clerk0.0.3-snapshot.v20260417132423

Published from 7d45a72

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417160442
PackageVersion
clerk0.0.3-snapshot.v20260417160442

Published from 9a70430

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 9a70430 to 1682ce6CompareApril 17, 2026 16:54
@rafa-thaytorafa-thayto changed the title fix(init): check auth before keyless/skip-auth promptsfix(init): simplify auth/keyless flow and fix --starterApr 17, 2026

@wyattjohwyattjoh left a comment

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.

Code Review — PR #175

Reviewed with Opus + Codex second-opinion validation.

Major

M1. Removing the bootstrap != null guard broadens keyless auto-select to existing projects(codex partial)
packages/cli-core/src/commands/init/index.ts:215-225 now returns keyless = true for any unauthenticated user on a keyless-capable framework, not only during bootstrap. Downstream effects:

  1. authenticateAndLink is skipped when users re-run clerk init signed out on an existing Clerk project.
  2. ctx.keyless = true at frameworks/helpers.ts:204-221,461-467 toggles permissive clerkMiddleware() (no route protection). Idempotency via hasClerkImport should prevent overwrites, but a scaffold-idempotency test for existingClerk: true + keyless: true would make the invariant load-bearing rather than incidental.
  3. env pull is skipped for the keyless branch.

The changeset does note keyless auto-select behavior, but the existing-project qualifier is not spelled out. Consider either restoring the if (!bootstrap) return false guard, or explicitly documenting the existing-project case in README and changeset.

M2. isAuthenticated() conflates "no credentials" with "network/API failure"(codex partial)
packages/cli-core/src/commands/init/heuristics.ts:139-147 delegates to getAuthenticatedEmail() which catches all errors and returns null. Expired tokens, Clerk 5xx, or offline laptops all silently demote the user into keyless (for keyless-capable frameworks) or skip-auth. Previously askSkipAuth gave the user a decision point. Consider a lighter credential-presence check (getToken() + CLERK_PLATFORM_API_KEY) for flow selection, reserving getAuthenticatedEmail() for the display label.

Minor

  • M3.isAuthenticated() is called twice on the authenticated + keyless-capable path (index.ts:90,94); resolve once and pass the boolean.
  • M4.packages/cli-core/src/commands/init/README.md:48-52 still documents the removed "Continue with temporary keys" prompt. Per .claude/rules/commands.md, the README must match behavior.
  • M5. New test "--starter without -y runs bootstrap interactively..." (index.test.ts:424-436) fully mocks promptAndBootstrap and never exercises the real guard at bootstrap.ts:173-177 that was the actual bug. Add a direct test for the guard.

Missed on first pass (codex caught)

  • Second README inconsistency.init/README.md:86 says non-keyless frameworks "always force authentication even with --yes", but index.ts:93-99 skips auth for unauthenticated users when -y is set.

Nits

  • isAuthenticated docstring (heuristics.ts:139-143) still references prompts that no longer exist.
  • BootstrapOverrides.implicitBootstrap doc (bootstrap.ts:135-140) doesn't note that skipConfirm supersedes it.
  • handleStarter (index.ts:153-163) dropping skipConfirm: true changes interactive --starter from previewPlan() to previewAndConfirm(). The changeset mentions --starter is "fully interactive", so this looks intentional; worth a bullet for reviewer transparency.

Positives

implicitBootstrap cleanly expresses "user opted into bootstrapping but did not opt out of prompts", better than overloading skipConfirm. Test additions cover the authentication x keyless x bootstrap/existing matrix well. isAuthenticated correctly short-circuits on CLERK_PLATFORM_API_KEY before network calls.

Skip the askSkipAuth prompt and keyless flow when the user is already
authenticated, and suppress the "keyless not supported" message outside
of the bootstrap flow.
`handleStarter` forced `skipConfirm: true` when calling the bootstrap
flow, which conflated "user already opted into bootstrapping" with
"non-interactive mode" inside `promptAndBootstrap` and caused
`clerk init --starter` (without `-y`) to fail the framework guard with:
"Non-interactive mode requires --framework for new projects".
Add a separate `implicitBootstrap` override that only skips the initial
"create a new one?" confirm, leaving `skipConfirm` to mean non-interactive.
Previously, resolveKeylessMode and the skipAuth check only consulted
getAuthenticatedEmail (OAuth). When CLERK_PLATFORM_API_KEY was set
(as in CI E2E), the user was treated as unauthenticated, so
`clerk init --yes` on a keyless-supporting framework fell through
to keyless mode instead of pulling the real keys — breaking every
E2E fixture.
Introduce an isAuthenticated helper that returns true for either
auth mechanism, and use it in both branches.
Previously, an unauthenticated user on a keyless-capable framework
was asked "Skip authentication for now?" with a default of yes.
The prompt added a step without meaningful choice in practice —
the unauthenticated path can't pull keys regardless, and the
keyless info message already points users to `clerk auth login`
for later.
Now the flow just follows auth state: signed in → authenticated
flow + env pull; not signed in → keyless. Removes `askSkipAuth`
entirely.
- Restore the `if (!bootstrap) return false` guard in `resolveKeylessMode`
so an unauthenticated re-run in an existing project falls through to the
authenticated flow (login + link + env pull) instead of silently going
keyless and skipping `env pull`.
- Make `isAuthenticated()` a pure credential-presence check (stored OAuth
token or `CLERK_PLATFORM_API_KEY`) so expired tokens, 5xx responses, or
an offline laptop no longer demote the user into keyless/skip-auth
without a prompt.
- Resolve auth once in `init()` and pass the boolean down.
- Add a direct `promptAndBootstrap` test that exercises the real
"non-interactive mode requires --framework" guard (previous coverage
fully mocked the boundary being tested).
- Update `init/README.md` and the changeset to describe the new behavior
and drop stale references to the removed "Skip authentication" prompt.
@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from ac7b139 to ea3b111CompareApril 20, 2026 15:04
@rafa-thayto
rafa-thayto merged commit 67a02d9 into mainApr 20, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/more-init-fixes branch April 20, 2026 15:08
@github-actionsgithub-actionsBot mentioned this pull request Apr 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(init): simplify auth/keyless flow and fix --starter - #175

Merged
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes
Apr 20, 2026
Merged

fix(init): simplify auth/keyless flow and fix --starter#175
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Four clerk init fixes on this branch:

  • Skip askSkipAuth and keyless flow when signed in. If the user has an OAuth token, clerk init goes straight to the authenticated flow (auth + link + env pull).
  • Count CLERK_PLATFORM_API_KEY as authenticated. Non-interactive / CI runs with an API key no longer fall through to keyless mode. A new isAuthenticated() helper covers both OAuth tokens and the env var.
  • Remove the "Skip authentication for now?" prompt. The prompt added a step without meaningful choice — the unauthenticated path can't pull keys either way. Now the flow just follows auth state: signed in → authenticated flow + env pull; not signed in → keyless automatically (the keyless info message already points users to clerk auth login for later).
  • Keep clerk init --starter interactive without --framework. Running --starter without -y no longer errors with "Non-interactive mode requires --framework" — it only implies "yes, bootstrap," not "skip every prompt."

Also suppresses the "keyless not supported" framework message outside the bootstrap flow.

Test plan

  • bun run test — init suite covers the permutations (auth vs. not, API key vs. OAuth, bootstrap vs. existing repo, --starter with and without -y)
  • bun run lint / bun run typecheck
  • CI E2E suite passes (was failing on Astro / Next / TanStack / React Router before the API-key fix)
  • Manual: clerk init on a keyless framework while signed in → authenticated flow, no keyless prompt
  • Manual: clerk init on a keyless framework while signed out → keyless flow, no prompt
  • Manual: clerk init in an existing repo with keyless framework while signed in → no keyless prompt, env pulled
  • Manual: clerk init --starter (no -y) → interactive framework picker instead of an error

@changeset-bot

changeset-botBot commented Apr 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea3b111

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rafa-thayto has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fcab2f5-4b16-4323-8e0f-2e9d72b71f17

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70430 and ea3b111.

📒 Files selected for processing (7)
  • .changeset/more-init-fixes.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/bootstrap.test.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/heuristics.ts
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
📝 Walkthrough

Walkthrough

The init command flow was changed to check authentication explicitly before skipping prompts. resolveKeylessMode now returns false when already authenticated and only enables keyless for unauthenticated, keyless-capable frameworks; it avoids logging “keyless not yet supported” unless bootstrap is requested. handleStarter and bootstrap calls now pass an implicitBootstrap flag instead of skipConfirm to bypass only the initial “create a new one?” confirmation. The exported askSkipAuth helper was removed and promptAndBootstrap gained an implicitBootstrap option. A new heuristics.isAuthenticated() helper was added (checks CLERK_PLATFORM_API_KEY or stored auth). Tests were updated to use isAuthenticated and to expand keyless/auth coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description check✅ PassedThe pull request description clearly and comprehensively explains the changes, including four specific fixes to the clerk init command with concrete examples and a detailed test plan.
Title check✅ PassedThe title accurately summarizes the main changes: fixing authentication/keyless flow logic and resolving the --starter flag issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417120320
PackageVersion
clerk0.0.3-snapshot.v20260417120320

Published from 5cf3c19

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 5cf3c19 to 7d45a72CompareApril 17, 2026 13:21
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417132423
PackageVersion
clerk0.0.3-snapshot.v20260417132423

Published from 7d45a72

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417160442
PackageVersion
clerk0.0.3-snapshot.v20260417160442

Published from 9a70430

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 9a70430 to 1682ce6CompareApril 17, 2026 16:54
@rafa-thaytorafa-thayto changed the title fix(init): check auth before keyless/skip-auth promptsfix(init): simplify auth/keyless flow and fix --starterApr 17, 2026

@wyattjohwyattjoh left a comment

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.

Code Review — PR #175

Reviewed with Opus + Codex second-opinion validation.

Major

M1. Removing the bootstrap != null guard broadens keyless auto-select to existing projects(codex partial)
packages/cli-core/src/commands/init/index.ts:215-225 now returns keyless = true for any unauthenticated user on a keyless-capable framework, not only during bootstrap. Downstream effects:

  1. authenticateAndLink is skipped when users re-run clerk init signed out on an existing Clerk project.
  2. ctx.keyless = true at frameworks/helpers.ts:204-221,461-467 toggles permissive clerkMiddleware() (no route protection). Idempotency via hasClerkImport should prevent overwrites, but a scaffold-idempotency test for existingClerk: true + keyless: true would make the invariant load-bearing rather than incidental.
  3. env pull is skipped for the keyless branch.

The changeset does note keyless auto-select behavior, but the existing-project qualifier is not spelled out. Consider either restoring the if (!bootstrap) return false guard, or explicitly documenting the existing-project case in README and changeset.

M2. isAuthenticated() conflates "no credentials" with "network/API failure"(codex partial)
packages/cli-core/src/commands/init/heuristics.ts:139-147 delegates to getAuthenticatedEmail() which catches all errors and returns null. Expired tokens, Clerk 5xx, or offline laptops all silently demote the user into keyless (for keyless-capable frameworks) or skip-auth. Previously askSkipAuth gave the user a decision point. Consider a lighter credential-presence check (getToken() + CLERK_PLATFORM_API_KEY) for flow selection, reserving getAuthenticatedEmail() for the display label.

Minor

  • M3.isAuthenticated() is called twice on the authenticated + keyless-capable path (index.ts:90,94); resolve once and pass the boolean.
  • M4.packages/cli-core/src/commands/init/README.md:48-52 still documents the removed "Continue with temporary keys" prompt. Per .claude/rules/commands.md, the README must match behavior.
  • M5. New test "--starter without -y runs bootstrap interactively..." (index.test.ts:424-436) fully mocks promptAndBootstrap and never exercises the real guard at bootstrap.ts:173-177 that was the actual bug. Add a direct test for the guard.

Missed on first pass (codex caught)

  • Second README inconsistency.init/README.md:86 says non-keyless frameworks "always force authentication even with --yes", but index.ts:93-99 skips auth for unauthenticated users when -y is set.

Nits

  • isAuthenticated docstring (heuristics.ts:139-143) still references prompts that no longer exist.
  • BootstrapOverrides.implicitBootstrap doc (bootstrap.ts:135-140) doesn't note that skipConfirm supersedes it.
  • handleStarter (index.ts:153-163) dropping skipConfirm: true changes interactive --starter from previewPlan() to previewAndConfirm(). The changeset mentions --starter is "fully interactive", so this looks intentional; worth a bullet for reviewer transparency.

Positives

implicitBootstrap cleanly expresses "user opted into bootstrapping but did not opt out of prompts", better than overloading skipConfirm. Test additions cover the authentication x keyless x bootstrap/existing matrix well. isAuthenticated correctly short-circuits on CLERK_PLATFORM_API_KEY before network calls.

Skip the askSkipAuth prompt and keyless flow when the user is already
authenticated, and suppress the "keyless not supported" message outside
of the bootstrap flow.
`handleStarter` forced `skipConfirm: true` when calling the bootstrap
flow, which conflated "user already opted into bootstrapping" with
"non-interactive mode" inside `promptAndBootstrap` and caused
`clerk init --starter` (without `-y`) to fail the framework guard with:
"Non-interactive mode requires --framework for new projects".
Add a separate `implicitBootstrap` override that only skips the initial
"create a new one?" confirm, leaving `skipConfirm` to mean non-interactive.
Previously, resolveKeylessMode and the skipAuth check only consulted
getAuthenticatedEmail (OAuth). When CLERK_PLATFORM_API_KEY was set
(as in CI E2E), the user was treated as unauthenticated, so
`clerk init --yes` on a keyless-supporting framework fell through
to keyless mode instead of pulling the real keys — breaking every
E2E fixture.
Introduce an isAuthenticated helper that returns true for either
auth mechanism, and use it in both branches.
Previously, an unauthenticated user on a keyless-capable framework
was asked "Skip authentication for now?" with a default of yes.
The prompt added a step without meaningful choice in practice —
the unauthenticated path can't pull keys regardless, and the
keyless info message already points users to `clerk auth login`
for later.
Now the flow just follows auth state: signed in → authenticated
flow + env pull; not signed in → keyless. Removes `askSkipAuth`
entirely.
- Restore the `if (!bootstrap) return false` guard in `resolveKeylessMode`
so an unauthenticated re-run in an existing project falls through to the
authenticated flow (login + link + env pull) instead of silently going
keyless and skipping `env pull`.
- Make `isAuthenticated()` a pure credential-presence check (stored OAuth
token or `CLERK_PLATFORM_API_KEY`) so expired tokens, 5xx responses, or
an offline laptop no longer demote the user into keyless/skip-auth
without a prompt.
- Resolve auth once in `init()` and pass the boolean down.
- Add a direct `promptAndBootstrap` test that exercises the real
"non-interactive mode requires --framework" guard (previous coverage
fully mocked the boundary being tested).
- Update `init/README.md` and the changeset to describe the new behavior
and drop stale references to the removed "Skip authentication" prompt.
@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from ac7b139 to ea3b111CompareApril 20, 2026 15:04
@rafa-thayto
rafa-thayto merged commit 67a02d9 into mainApr 20, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/more-init-fixes branch April 20, 2026 15:08
@github-actionsgithub-actionsBot mentioned this pull request Apr 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(init): simplify auth/keyless flow and fix --starter - #175

Merged
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes
Apr 20, 2026
Merged

fix(init): simplify auth/keyless flow and fix --starter#175
rafa-thayto merged 6 commits into
mainfrom
rafa-thayto/more-init-fixes

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Four clerk init fixes on this branch:

  • Skip askSkipAuth and keyless flow when signed in. If the user has an OAuth token, clerk init goes straight to the authenticated flow (auth + link + env pull).
  • Count CLERK_PLATFORM_API_KEY as authenticated. Non-interactive / CI runs with an API key no longer fall through to keyless mode. A new isAuthenticated() helper covers both OAuth tokens and the env var.
  • Remove the "Skip authentication for now?" prompt. The prompt added a step without meaningful choice — the unauthenticated path can't pull keys either way. Now the flow just follows auth state: signed in → authenticated flow + env pull; not signed in → keyless automatically (the keyless info message already points users to clerk auth login for later).
  • Keep clerk init --starter interactive without --framework. Running --starter without -y no longer errors with "Non-interactive mode requires --framework" — it only implies "yes, bootstrap," not "skip every prompt."

Also suppresses the "keyless not supported" framework message outside the bootstrap flow.

Test plan

  • bun run test — init suite covers the permutations (auth vs. not, API key vs. OAuth, bootstrap vs. existing repo, --starter with and without -y)
  • bun run lint / bun run typecheck
  • CI E2E suite passes (was failing on Astro / Next / TanStack / React Router before the API-key fix)
  • Manual: clerk init on a keyless framework while signed in → authenticated flow, no keyless prompt
  • Manual: clerk init on a keyless framework while signed out → keyless flow, no prompt
  • Manual: clerk init in an existing repo with keyless framework while signed in → no keyless prompt, env pulled
  • Manual: clerk init --starter (no -y) → interactive framework picker instead of an error

@changeset-bot

changeset-botBot commented Apr 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea3b111

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Apr 16, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rafa-thayto has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fcab2f5-4b16-4323-8e0f-2e9d72b71f17

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70430 and ea3b111.

📒 Files selected for processing (7)
  • .changeset/more-init-fixes.md
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/bootstrap.test.ts
  • packages/cli-core/src/commands/init/bootstrap.ts
  • packages/cli-core/src/commands/init/heuristics.ts
  • packages/cli-core/src/commands/init/index.test.ts
  • packages/cli-core/src/commands/init/index.ts
📝 Walkthrough

Walkthrough

The init command flow was changed to check authentication explicitly before skipping prompts. resolveKeylessMode now returns false when already authenticated and only enables keyless for unauthenticated, keyless-capable frameworks; it avoids logging “keyless not yet supported” unless bootstrap is requested. handleStarter and bootstrap calls now pass an implicitBootstrap flag instead of skipConfirm to bypass only the initial “create a new one?” confirmation. The exported askSkipAuth helper was removed and promptAndBootstrap gained an implicitBootstrap option. A new heuristics.isAuthenticated() helper was added (checks CLERK_PLATFORM_API_KEY or stored auth). Tests were updated to use isAuthenticated and to expand keyless/auth coverage.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 50.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Description check✅ PassedThe pull request description clearly and comprehensively explains the changes, including four specific fixes to the clerk init command with concrete examples and a detailed test plan.
Title check✅ PassedThe title accurately summarizes the main changes: fixing authentication/keyless flow logic and resolving the --starter flag issue.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417120320
PackageVersion
clerk0.0.3-snapshot.v20260417120320

Published from 5cf3c19

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 5cf3c19 to 7d45a72CompareApril 17, 2026 13:21
@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417132423
PackageVersion
clerk0.0.3-snapshot.v20260417132423

Published from 7d45a72

@rafa-thayto

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Snapshot published

npm install -g clerk@0.0.3-snapshot.v20260417160442
PackageVersion
clerk0.0.3-snapshot.v20260417160442

Published from 9a70430

@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from 9a70430 to 1682ce6CompareApril 17, 2026 16:54
@rafa-thaytorafa-thayto changed the title fix(init): check auth before keyless/skip-auth promptsfix(init): simplify auth/keyless flow and fix --starterApr 17, 2026

@wyattjohwyattjoh left a comment

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.

Code Review — PR #175

Reviewed with Opus + Codex second-opinion validation.

Major

M1. Removing the bootstrap != null guard broadens keyless auto-select to existing projects(codex partial)
packages/cli-core/src/commands/init/index.ts:215-225 now returns keyless = true for any unauthenticated user on a keyless-capable framework, not only during bootstrap. Downstream effects:

  1. authenticateAndLink is skipped when users re-run clerk init signed out on an existing Clerk project.
  2. ctx.keyless = true at frameworks/helpers.ts:204-221,461-467 toggles permissive clerkMiddleware() (no route protection). Idempotency via hasClerkImport should prevent overwrites, but a scaffold-idempotency test for existingClerk: true + keyless: true would make the invariant load-bearing rather than incidental.
  3. env pull is skipped for the keyless branch.

The changeset does note keyless auto-select behavior, but the existing-project qualifier is not spelled out. Consider either restoring the if (!bootstrap) return false guard, or explicitly documenting the existing-project case in README and changeset.

M2. isAuthenticated() conflates "no credentials" with "network/API failure"(codex partial)
packages/cli-core/src/commands/init/heuristics.ts:139-147 delegates to getAuthenticatedEmail() which catches all errors and returns null. Expired tokens, Clerk 5xx, or offline laptops all silently demote the user into keyless (for keyless-capable frameworks) or skip-auth. Previously askSkipAuth gave the user a decision point. Consider a lighter credential-presence check (getToken() + CLERK_PLATFORM_API_KEY) for flow selection, reserving getAuthenticatedEmail() for the display label.

Minor

  • M3.isAuthenticated() is called twice on the authenticated + keyless-capable path (index.ts:90,94); resolve once and pass the boolean.
  • M4.packages/cli-core/src/commands/init/README.md:48-52 still documents the removed "Continue with temporary keys" prompt. Per .claude/rules/commands.md, the README must match behavior.
  • M5. New test "--starter without -y runs bootstrap interactively..." (index.test.ts:424-436) fully mocks promptAndBootstrap and never exercises the real guard at bootstrap.ts:173-177 that was the actual bug. Add a direct test for the guard.

Missed on first pass (codex caught)

  • Second README inconsistency.init/README.md:86 says non-keyless frameworks "always force authentication even with --yes", but index.ts:93-99 skips auth for unauthenticated users when -y is set.

Nits

  • isAuthenticated docstring (heuristics.ts:139-143) still references prompts that no longer exist.
  • BootstrapOverrides.implicitBootstrap doc (bootstrap.ts:135-140) doesn't note that skipConfirm supersedes it.
  • handleStarter (index.ts:153-163) dropping skipConfirm: true changes interactive --starter from previewPlan() to previewAndConfirm(). The changeset mentions --starter is "fully interactive", so this looks intentional; worth a bullet for reviewer transparency.

Positives

implicitBootstrap cleanly expresses "user opted into bootstrapping but did not opt out of prompts", better than overloading skipConfirm. Test additions cover the authentication x keyless x bootstrap/existing matrix well. isAuthenticated correctly short-circuits on CLERK_PLATFORM_API_KEY before network calls.

Skip the askSkipAuth prompt and keyless flow when the user is already
authenticated, and suppress the "keyless not supported" message outside
of the bootstrap flow.
`handleStarter` forced `skipConfirm: true` when calling the bootstrap
flow, which conflated "user already opted into bootstrapping" with
"non-interactive mode" inside `promptAndBootstrap` and caused
`clerk init --starter` (without `-y`) to fail the framework guard with:
"Non-interactive mode requires --framework for new projects".
Add a separate `implicitBootstrap` override that only skips the initial
"create a new one?" confirm, leaving `skipConfirm` to mean non-interactive.
Previously, resolveKeylessMode and the skipAuth check only consulted
getAuthenticatedEmail (OAuth). When CLERK_PLATFORM_API_KEY was set
(as in CI E2E), the user was treated as unauthenticated, so
`clerk init --yes` on a keyless-supporting framework fell through
to keyless mode instead of pulling the real keys — breaking every
E2E fixture.
Introduce an isAuthenticated helper that returns true for either
auth mechanism, and use it in both branches.
Previously, an unauthenticated user on a keyless-capable framework
was asked "Skip authentication for now?" with a default of yes.
The prompt added a step without meaningful choice in practice —
the unauthenticated path can't pull keys regardless, and the
keyless info message already points users to `clerk auth login`
for later.
Now the flow just follows auth state: signed in → authenticated
flow + env pull; not signed in → keyless. Removes `askSkipAuth`
entirely.
- Restore the `if (!bootstrap) return false` guard in `resolveKeylessMode`
so an unauthenticated re-run in an existing project falls through to the
authenticated flow (login + link + env pull) instead of silently going
keyless and skipping `env pull`.
- Make `isAuthenticated()` a pure credential-presence check (stored OAuth
token or `CLERK_PLATFORM_API_KEY`) so expired tokens, 5xx responses, or
an offline laptop no longer demote the user into keyless/skip-auth
without a prompt.
- Resolve auth once in `init()` and pass the boolean down.
- Add a direct `promptAndBootstrap` test that exercises the real
"non-interactive mode requires --framework" guard (previous coverage
fully mocked the boundary being tested).
- Update `init/README.md` and the changeset to describe the new behavior
and drop stale references to the removed "Skip authentication" prompt.
@rafa-thayto
rafa-thaytoforce-pushed the rafa-thayto/more-init-fixes branch from ac7b139 to ea3b111CompareApril 20, 2026 15:04
@rafa-thayto
rafa-thayto merged commit 67a02d9 into mainApr 20, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/more-init-fixes branch April 20, 2026 15:08
@github-actionsgithub-actionsBot mentioned this pull request Apr 20, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rafa-thayto@wyattjoh