Repository navigation
feat(init): Supabase/Clerk provider switch, integrations connect, brand from --app-url, doctor --project (C6, 0.23.0) - #63
Open
thedogwiththedataonit wants to merge 44 commits into
Conversation
thedogwiththedataonit
force-pushed
the
claude/init-providers
branch
from
October 17, 2026 08:22
a2ea384 to
215f1d4
Compare
thedogwiththedataonit
force-pushed
the
claude/migrate-resend
branch
from
October 20, 2026 16:40
e5a5052 to
83beb90
Compare
thedogwiththedataonit
force-pushed
the
claude/init-providers
branch
from
October 20, 2026 16:42
215f1d4 to
c67d137
Compare
This was referenced Oct 20, 2026
thedogwiththedataonit
added a commit
that referenced
this pull request
Oct 21, 2026
The DX stack is one platform PR now, so the bundled spec is its whole openapi/public-api-v1.yaml (brew-v2 main 2db5428b09 plus every DX route) instead of main plus per-PR deltas. Types regenerated. It also carries the hosted auth hook connection routes, which `integrations connect` / `integrations connection` bind in #63: they sit in the spec parity test's pending-build list until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit
force-pushed
the
claude/migrate-resend
branch
from
October 21, 2026 04:12
83beb90 to
dd52162
Compare
thedogwiththedataonit
force-pushed
the
claude/init-providers
branch
from
October 21, 2026 04:12
c67d137 to
8702f7c
Compare
thedogwiththedataonit
added a commit
that referenced
this pull request
Oct 25, 2026
The DX stack is one platform PR now, so the bundled spec is its whole openapi/public-api-v1.yaml (brew-v2 main 2db5428b09 plus every DX route) instead of main plus per-PR deltas. Types regenerated. It also carries the hosted auth hook connection routes, which `integrations connect` / `integrations connection` bind in #63: they sit in the spec parity test's pending-build list until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit
force-pushed
the
claude/migrate-resend
branch
from
October 25, 2026 13:35
dd52162 to
d976e43
Compare
thedogwiththedataonit
force-pushed
the
claude/init-providers
branch
from
October 25, 2026 13:35
8702f7c to
3845aab
Compare
thedogwiththedataonit
added a commit
that referenced
this pull request
Oct 31, 2026
The DX stack is one platform PR now, so the bundled spec is its whole openapi/public-api-v1.yaml (brew-v2 main 2db5428b09 plus every DX route) instead of main plus per-PR deltas. Types regenerated. It also carries the hosted auth hook connection routes, which `integrations connect` / `integrations connection` bind in #63: they sit in the spec parity test's pending-build list until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit
force-pushed
the
claude/migrate-resend
branch
from
October 31, 2026 21:21
d976e43 to
ffd03f1
Compare
thedogwiththedataonit
force-pushed
the
claude/init-providers
branch
from
October 31, 2026 21:21
3845aab to
ab91520
Compare
thedogwiththedataonit
force-pushed
the
claude/migrate-resend
branch
from
November 1, 2026 03:00
ffd03f1 to
5946e07
Compare
thedogwiththedataonit
force-pushed
the
claude/init-providers
branch
from
November 1, 2026 03:02
ab91520 to
a82d470
Compare
thedogwiththedataonit
added a commit
that referenced
this pull request
Nov 1, 2026
The DX stack is one platform PR now, so the bundled spec is its whole openapi/public-api-v1.yaml (brew-v2 main 2db5428b09 plus every DX route) instead of main plus per-PR deltas. Types regenerated. It also carries the hosted auth hook connection routes, which `integrations connect` / `integrations connection` bind in #63: they sit in the spec parity test's pending-build list until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit
force-pushed
the
claude/migrate-resend
branch
from
November 1, 2026 04:45
d432541 to
8c1132d
Compare
thedogwiththedataonit
force-pushed
the
claude/init-providers
branch
from
November 1, 2026 04:54
514b502 to
af61413
Compare
#55 now sets up nothing that routes the auth library's emails without the auth set. The provider step does the same: with --sets lifecycle, Supabase and Clerk keep their auth emails (no hook or delivery steps), and --configure-supabase or --configure-clerk exits 2 before any request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#55's walk over init's value flags now covers the provider flags. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When --app-url picks a brand the app's key in .env.local is not bound to, the client still acts as the old brand: --domain no longer lists or adds a domain there. The step is skipped until a re-run with the right key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… checks readiness - --app-url asks to replace the app key in .env.local only when that key is bound to another brand than the one picked (GET /v1/brand with the app key), not whenever the organization key had to switch. - doctor --project reads each key's readiness: a published key that cannot send (DOMAIN_REQUIRED, AUTOMATION_MISSING) fails with its blockers, and a draft over a live version no longer reads as unpublished. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xposure doctor --project now checks the process environment as well as the .env files: a NEXT_PUBLIC_/VITE_/PUBLIC_/EXPO_PUBLIC_ name holding a Brew key there ships with the build too. Values are never printed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like app-emails kit: the full response still prints, and the exit code tells a script the provider's auth emails are not all ready. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When `init --app-url` cannot read the key's brand (a 503 after retries), the brand step failed but brandReady stayed true, so the domain, kit and provider steps ran on the unswitched client: an organization key acting as brand A could add --domain and set up the kit there for an app that belongs to brand B. A failed lookup now sets brandReady to false, so those steps are skipped until a re-run (exit 1 for the failed step). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
patchesHeld held a reviewed patch only when the files or kit step failed. A kit skipped because the brand is not ready (a failed lookup, a brand still extracting, an app key bound to another brand) let a pinned auth patch apply, wiring auth emails to kit keys that do not exist yet. Patches are now held while brandReady is false too; they stay pending with their apply next step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--rollback-provider reported the state step done and exited 0 even when writer.writeFile refused brew.json (a symbolic link out of the project), so the restored provider's record stayed and a later run could repeat the rollback. A refused write is now a failed state step naming why (exit 1), as the .env.local step already reports one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A connect re-run leaves an auth email someone unpublished in Brew off, and a sandbox sender reaches the team only, yet the human output called every key that set up "ready". It now says "set up" and points at app-emails list for which ones send; the response is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After the key worked, a 503 or connection error listing app emails, domains or the hook connection became skipped, so doctor --project could exit 0 without checking them. Those reads now fail the check; skipped is left for checks that do not apply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Configuring another project saves its hook settings to brew.json and its secret to .env.local before connecting. When an auth email was not ready, or the readiness read failed, the step returned the earlier project's settings, so brew.json and the backed-up secret described different hooks and a rollback would restore one hook with the other's secret. Those results now keep the settings that were saved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
init installs no @brew.new/sdk in a browser-only Vite app or Supabase Edge Functions (they send from the server or import npm:@brew.new/sdk), yet doctor --project failed those apps for not declaring it. The SDK check is now skipped for them, from the framework brew.json records. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ject picked The rerun-supabase-connect command (an auth email not ready yet) and the configure-supabase-hook command dropped --supabase-project, so a re-run detected the project again from the app's files and could change another project's Send Email hook, or find none. Both now carry the picked ref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd step init --app-url with an organization key acting as brand A answered a failed GET /v1/brands as a website mismatch on A: the brand step was done and the domain, kit and provider steps ran in A, though the app may be another brand's. A failed list now fails the brand step (exit 1), holds those steps like a failed brand lookup, and asks for a re-run or --brand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The printed integrations connect / connection commands resolve their own key and brand (the CLI's saved key, BREW_BRAND_ID, BREW_API_URL), not the app key in .env.local, so after an init for brand B they could connect the CLI's brand A, or another server. They and the init follow-ups (--configure-clerk, --configure-supabase, --rollback-provider) now carry --brand and --api-url when a plain run would resolve another brand or server; never a credential. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#55's failed-install test types its spawn as SpawnFn; this PR moved the init test helpers out of init.test.ts and its SpawnFn import with them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e verified The domain check read the first 100 domains only, so a brand whose verified sending domain sat on a later page was told it had none and sent to the DNS setup. It now pages through them like init does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
doctor --project paired the app key from .env.local with the server from --api-url, BREW_API_URL or the CLI config, so for an app set up against another server it sent that key elsewhere and reported that account. It now uses the BREW_API_URL saved beside the key (none = the default), as init does, and refuses another server named explicitly (exit 2) before any request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The printed init follow-ups (--configure-clerk, --configure-supabase, --rollback-provider) kept --app and the brand and server but dropped --stack, so a run that needed --stack because detection was wrong printed a command that detected the old stack and exited 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ure check looksLikeKey flagged every browser-exposed name containing BREW, so VITE_BREW_API_URL=https://brew.new/api or an empty VITE_BREW_API_KEY placeholder failed doctor --project with advice to revoke a key. It now needs a value, and either a Brew key-like name (KEY, SECRET, TOKEN), a brew_ value, or the app key itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When --app-url picks a brand with an organization key, init asks which brand the app key in .env.local is bound to. That client used the run's server, so with --api-url (or BREW_API_URL) for another server the app key went there. It now asks only the server saved beside the key (BREW_API_URL in .env.local, none = the default). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…found The app-emails check stopped after 20 pages even when more were left, so a brand with over 2,000 app emails could see working keys reported as missing. It now reads until every key in brew.json is found or the pages end (a repeated cursor ends it too). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mail page Two Greptile findings on #63, each with a test that was red first. P1: a --configure-supabase run for another project backed up that project's hook secret in .env.local before brew.json saved the new settings. When brew.json refused the write (a link outside the project), it kept the first project's settings but .env.local now held the other project's secret, so --rollback-provider would restore the first hook with the wrong secret. The step now remembers the backup it replaces and puts it back (or removes the new one) when the save fails. backupHookSecret takes undefined to remove the backup; savedHookSecret reads it. P2: publicKeys stopped after 20 pages of GET /v1/app-emails, so on a brand with more than 2,000 app emails a ready auth key on a later page counted as not ready, and Supabase setup refused every run (or Clerk kept its templates). It now reads until every key the provider needs has its row or the list ends, and stops on a repeated cursor instead of a page cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rand resolveAppBrand stopped after 10 pages of 100 brands, so a matching brand past the first 1,000 was missed and init tried to create another, which Brew refuses as BRAND_DOMAIN_CONFLICT with a generic step. The lookup now reads pages until a match or the list's end; a cursor seen before ends it, so a server repeating one cannot loop it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With an organization key on one server and .env.local's app key (with its BREW_API_URL) on another, the --app-url brand check asked the app key's server which brand the key is bound to and compared only the id. A staging copy with production's brand ids passed, so the domain, kit and provider setup ran on the organization key's server while the app kept sending to the other. The app key now holds the setup unless it is on this run's server and bound to the brand picked; the replace-app-key step names both servers when they differ, and the key is not sent anywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is needed The app-key check ran only when --app-url switched the run to another brand. When the run's own key (--api-key or BREW_API_KEY) already had the app's brand, an app key in .env.local on another server, or bound to another brand, still let the domain, kit and provider setup run where the app does not send. The check now covers the brand the run sets up either way, whenever .env.local's key is not the key the run acts as. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #61 (head
d3eef9f; restacked 11-01); needs brew-v2 #2169 deployed and SDK 13.7.0, after #61's gate.Chain: #44 → #53 → #54 → #55 (init) → #61 (migrate resend) → this PR (C6, 0.23.0). Base:
claude/migrate-resend.Summary
The CLI pieces init was still missing (plan C4 "provider config", brand from
--app-url, the domain link,doctor --project,integrations connect).integrations connect <supabase_auth|clerk>binds the SDK'sintegrations.connect(typescript-sdk#74, 13.7.0, not released). Destructive class. The signing secret comes from--signing-secret-env <VAR>or--signing-secret-stdin, never from argv. It is never echoed, including when it is pasted where the variable name goes. Prints{connectionId, webhookUrl, status, authEmails}.integrations connection <provider>bindsintegrations.getConnection.init --configure-supabase(needsSUPABASE_ACCESS_TOKENlocally; the token goes to api.supabase.com only):brew.json#providerConfigbefore changing anything (a previous hook secret goes to.env.local, neverbrew.json);v1,whsec_secret;/v1/projects/{ref}/config/authwithhook_send_email_enabled,hook_send_email_uriandhook_send_email_secrets.--dry-run --jsonshows the exact PATCH with the secret replaced. A re-run makes no change.init --configure-clerk(needsCLERK_SECRET_KEYin the shell or.env.local) runs only onceintegrations connection clerkisactive. It then turns off delivery throughPOST /templates/email/{slug}/toggle_deliveryfor exactly theCLERK_EMAIL_SLUG_ROUTESslugs whose key has a published app email. Before the connection is active it refuses and makes no Clerk call. A bare--yesnever touches Clerk or Supabase.init --rollback-providerrestores what either flag changed, using whatbrew.jsonsaved.Without those flags, init prints the manual dashboard steps. The self-hosted Edge Function and its local
config.tomlpatch become the optional alternative.--app-url: with an organization key, init reuses the brand whose website matches, or creates one throughPOST /v1/brands(idempotent) and waits for it to be ready. A 403 (ORG_SCOPE_REQUIRED) gets a clear next step. A key bound to one brand is never moved; a mismatch is only reported.--domain: also returns the domain's page in Brew (domain.connectUrl). The report says plainly that Connect automatically (Entri) runs in the app, because v1 has no API for it, and that the sandbox applies until the domain verifies.doctor --project [--json]checks:BREW_API_KEYis present, git-ignored, and never underNEXT_PUBLIC_,VITE_,PUBLIC_orEXPO_PUBLIC_;SDK_RANGE(^13.6.0);brew.jsonkey exists and is published (GET /v1/app-emails);Exit codes: 0, 1 if any check fails, 2 if there is no
brew.json.Spec: the final brew-v2 #2169 head
6e4accea4cwhole, from send by key, app-emails, trigger keys (C2, 0.20.0) #54 (sha256b86fb228250266ea57637a3d5c1e722e05e289a9d59b0c9b349bcc1243178bd3). History (superseded 10-21 by brew-v2 #2169 @6f8ef50544): migrate resend (C5, 0.22.0): move a Resend account and codebase to Brew #61's spec (brew-v2 #2145 b2912ad9f7: #2066 0fed90fbf3 merged with #2064 9f97aea3f8, plus #2145'ssourcemodes) plus exactly the deltas from #2068 (12a02af5e3 over 0fed90fbf3) and #2146 (c3da555e60 over 12a02af5e3). Re-checked on the restack: byte-identical to a three-way merge of #2145's and #2146's files over #2066's. Types are regenerated.The init report stays schema v1 with additive fields:
providers[], aproviderstep,brand.domain/matchesAppUrl/created, anddomain.connectUrl.vendorFetchnow also redacts secrets carried in the request body. The init test helpers move totests/helpers/init-api.ts, which keeps the test files from growing past 1,000 lines.External APIs (verified against the official references; cited in code comments):
GET|PATCH https://api.supabase.com/v1/projects/{ref}/config/auth(https://supabase.com/docs/reference/api/v1-update-auth-service-config, https://supabase.com/docs/reference/api/v1-get-auth-service-config, spec https://api.supabase.com/api/v1-json). The secret format follows https://supabase.com/docs/guides/auth/auth-hooks/send-email-hook.GET /v1/templates/email/{slug}andPOST /v1/templates/email/{slug}/toggle_deliverywith{delivered_by_clerk}(https://clerk.com/docs/reference/backend-api, spec https://github.com/clerk/openapi-specs/blob/main/bapi/2026-08-20.yml). Clerk's spec marks both endpoints deprecated (in favour of the dashboard), but they are still served. On a 402 or 423 the CLI points the person to the dashboard.SDK and publish gate
This uses the SDK methods, not raw HTTP. Local validation ran against the typescript-sdk#74 build (
b4508d8, on #643c3827f).package.jsonkeeps main's published SDK range, so CI typecheck and parity stay red until SDK 13.7.0 is on npm.RELEASING.mdhas a gate for this: publish SDK 13.7.0, runbun add @brew.new/sdk@^13.7.0, then a frozen install and the full checks. brew-v2 #2169 must also be deployed first, and the live openapi.yaml compared with the bundled one. The restack kept both RELEASING.md sections (#61's API gate, then this one). Generated app code still asks for^13.6.0.Test plan
bun run tsc,bun run lintbun run test: 100 files, 1495 tests (head956fbcb, 11-01). New suites:integrations-connect,init-providers,init-app-url,doctor-project,lib/init/providers. Both parity sentinels pass.docs:commands:check,generate:skills:check,sync:launchers:check,generate:typesfreshnessbrew.json, and no Brew request carries a provider token.--yes. Clerk refusal while the connection is pending. 403 fromPOST /v1/brands. 404 for a provider that is not connected. Supabase vendor errors are redacted.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.

Confidence Score: 5/5
The reviewed changes appear safe to merge under the documented SDK and deployment gates.Summary
Adds hosted Supabase and Clerk connections, provider setup and rollback, brand selection from
--app-url, anddoctor --project.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Resolve brand from app URL] --> B{Saved app key differs from run key?} B -->|No| E[Continue when brand is ready] B -->|Yes| C{Server and brand match?} C -->|Yes| E C -->|No| D[Ask user to replace app key] D --> F[Hold domain, kit, patches and provider setup]Reviews (45) · Last reviewed commit: "fix(init): check the app key's server an..." · Reviewed by Greptile