Skip to content

send by key, app-emails, trigger keys (C2, 0.19.0) - #54

Open
thedogwiththedataonit wants to merge 11 commits into
claude/browser-loginfrom
claude/send-app-emails
Open

thedogwiththedataonit wants to merge 11 commits into
claude/browser-loginfrom
claude/send-app-emails

Conversation

@thedogwiththedataonit

@thedogwiththedataonit thedogwiththedataonit commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Update 2026-10-25: restacked on #53 (CLI main 9250732). Conflicts: vendored spec + generated types (re-vendored), RELEASING.md (main's contact-import SDK minimum kept beside the app email SDK gate), generated docs. The vendored spec is the brew-v2 #2169 @ 6f8ef50544 DX delta on main's spec (brew-v2 87ac2c6242), structurally merged (no conflicts; one enum union). Checks against a local SDK build of typescript-sdk#64 merged with SDK main: tsc, lint, test 962, generated types fresh, docs:commands:check, generate:skills:check, build, bin smoke, signal smoke ✓. CI typecheck/parity stay red until SDK 13.6.0 is on npm (gate in RELEASING.md).

Renumbered 10-25: main published 0.18.0 (--lang/--clear-lang, contact imports, SDK ^13.3.0), and SDK main published 13.1.0, 13.2.0 and 13.3.0 without the DX features (13.4.0 is reserved for its pending email lang release). So this chain moves up one: #54 0.19.0, #55 0.20.0, #61 0.21.0, #63 0.22.0, and the DX SDK releases are send-by-key 13.5.0, adapters/appEmails 13.6.0 (SDK_RANGE ^13.6.0, so installs can no longer resolve to the published 13.3.0), integrations.connect 13.7.0. This PR is 0.19.0; older numbers below are pre-rebase.

Spec sync 2026-10-21: restacked on CLI main 2ce7d42. The vendored spec is now brew-v2 #2169 @ 6f8ef50544 whole (main 2db5428b09 plus every DX route; spec source: brew-v2 #2169 @ 6f8ef50544), replacing the per-PR deltas; the hosted auth hook connection routes sit in the parity pending list until #63. Adapted: kit hints per refusal (APP_EMAIL_DOMAIN_PURPOSE_MISMATCH → transactional --domain or none, APP_EMAIL_TRIGGER_IN_USE → unpublish details.automationIds, APP_EMAIL_EDITED_IN_BREW → --overwrite), upsert renders DRAFT_SAVED_NOT_LIVE as "the published version keeps sending", --domain help names the purpose refusal; MSW tests for those plus DKIM_SELECTOR_IN_USE + suggestedNames on domains add. RELEASING: brew-v2 #2169 deployed. Checks: tsc, lint, test 931, generated types fresh, docs/skills freshness, build, signal smoke ✓.

Renumbered 10-21: main published 0.17.0 (canvas commands, SDK ^13.0.0), so this chain moved up one: #54 0.18.0, #55 0.19.0, #61 0.20.0, #63 0.21.0. This PR is 0.18.0; older numbers below are pre-rebase.

Stacked on #53 (→ #44 → main). CLI 0.17.0 (C2 of the DX push): send by key, app emails, trigger keys. This release also carries #44 and #53.

Depends on unpublished work. send, app-emails and the types registry call brew.send / brew.appEmails, which no published SDK has (npm 12.2.0 / 12.3.0 are other features). package.json and bun.lock stay on main's SDK git pin on purpose (never a hand-written lockfile entry), so once this targets main, CI typecheck and parity fail on these commands until the SDK release exists. RELEASING.md now has an app email SDK gate with the steps. Local checks run against the typescript-sdk#64 worktree build (SDK main + #55 + #64), linked into node_modules; nothing committed.

Publish order (nothing here is published, merged or tagged)

  1. brew-v2: #2169 (the whole DX stack collapsed into one PR: browser sign-in, trigger keys, fire by key, team sandbox, app emails, import modes, hosted auth hooks) merged and deployed.
  2. typescript-sdk: init + the brew.new / create-brew launchers (C4, 0.20.0) #55 (send, fireByKey) and Release CLI 0.17.0 with registry SDK 13 #64 (appEmails, adapter subpaths) rebased onto SDK main (which already has the canvas revocation fix this CLI's main needs) and published. Numbers are final: init + the brew.new / create-brew launchers (C4, 0.20.0) #55 ships as 13.1.0 and Release CLI 0.17.0 with registry SDK 13 #64 as 13.2.0, after the canvas fix's 13.0.0; this CLI takes ^13.2.0. Both SDK PRs are behind SDK main (init + the brew.new / create-brew launchers (C4, 0.20.0) #55 conflicts).
  3. This chain: bun add @brew.new/sdk@^<that version> (regenerates bun.lock from the registry, replacing main's git pin), re-vendor openapi/public-api-v1.yaml from brew-v2 main (once #2169 merges), frozen install + full checks, then CLI 0.17.0 → 0.18.0 → 0.19.0.
  4. Launchers: reserve brew.new and create-brew on npm (both still 404) for the token's account; until then the release step warns and skips them.

What

  • send <key> → brew.send (POST /v1/automations/triggers/by-key/{triggerKey}/fire). --to sets payload.email (wins over --data), --data <json|->, --idempotency-key (route is fail_closed). Destructive (exit 4 without --yes). automations.triggers.fireByKey → SDK_SKIP_LIST (alias of send).
  • app-emails list | get <key> | upsert <key> | kit bind GET /v1/app-emails, GET|PUT /v1/app-emails/{triggerKey}, POST /v1/app-emails/kit. upsert (starter or --source-file with --format html/mjml/jsx, --overwrite, --no-publish) and kit (--sets, --stack, --keys, --domain, --overwrite, --idempotency-key) are destructive because they publish; kit exits 1 when a key failed. Both need the automations and emails scopes (stated in help, README and the skill).
  • --key on automations triggers create|update; KEY column in triggers list.
  • types appends the BrewTriggerKeyRegistry augmentation for keyed triggers on top of main's contract-artifact types (brew-cli#58); a key-less workspace gets the same bytes as --no-registry. listAllTriggers, fetchContractArtifacts and buildContractsFile are exported for init.
  • Vendored spec = brew-v2 claude/app-emails-setup 84cea75be5 exactly (CLI main's spec is brew-v2 main 8c7020e535 byte for byte; #2066 sits on #2013 e499465757, #2017 54a862fee0 and #2059 4792d4a323), so it carries secret on custom payloadSchema fields. Generated types regenerated.
  • 10-16 sync: SDK gate text and 0.17.0 changelog name 13.1.0/13.2.0; app-emails upsert passes --input fields (incl. secret: true) through, pinned by a test and an example; a test pins send's not-started line naming DOMAIN_REQUIRED, which SDK 13.2.0 types (its spec now includes #2059).
  • Version 0.17.0; CHANGELOG ## 0.17.0 (with the Project foundations + mcp install (C1) #44/Login through the browser (session flow) (C3) #53 notes and main's canvas note), README, skill, command docs.

Review round 2026-10-16

  • Rebased onto the new Login through the browser (session flow) (C3) #53 (main 0.16.0). Conflicts: the vendored spec (re-vendored as above), types.ts (registry rebuilt on main's contract artifacts), package.json (main's SDK pin kept), src/lib/long-running.ts (the app-email deadlines use main's sdk.* namespace), generated docs.
  • Fixes: upsert --input - always confirms (stdin could set overwrite); kit rejects an empty --keys/--sets (was "whole kit") and --keys with --sets/--stack, and its summary reads inline --input; the trigger key grammar, --format, --sets, --stack and kit keys are checked locally (exit 2, not 1), with a test pinning them to the vendored spec.

Checks (scratch SDK = SDK main + #64)

tsc ✓ · lint ✓ · test 59 files / 824 tests ✓ (parity-sdk + parity-spec green) · generated types fresh ✓ · docs:commands:check ✓ · generate:skills:check ✓. CI runs only on PRs into main.

Live validation owed (after deploy + publish)

send to an org member in sandbox and to a non-member (DOMAIN_REQUIRED), app-emails kit --stack better-auth, upsert re-run and 409 APP_EMAIL_EDITED_IN_BREW, types on a brand with kit keys.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

RetriggerView in GreptileConfidence Score: 4/5

Do not merge yet: mixed kit selections can still publish different emails than the flags request.

Fix All in Claude CodeFindings

  1. P1 Selection flags get ignored ▶
  2. P2 Recovery hides the needed IDs ▶
  3. P2 Two flags consume stdin ▶
  4. P2 Recovery repeats failed results ▶
  5. P2 Setup code is hidden ▶
  6. P2 Type checks lack compile coverage ▶
Fix with agent prompt
### Issue 1
src/commands/app-emails/kit.ts:94-100
With `app-emails kit --sets lifecycle --input '{"keys":["auth.otp"]}' --yes`, the conflict check sees only the flags and passes. `mergeInput` keeps `keys`, which takes precedence over `sets`, so the command publishes `auth.otp` instead of the lifecycle emails requested by the flag.

Check for conflicting selections after merging `--input`, before calling the SDK.

### Issue 2
src/commands/app-emails/kit.ts:194
The `APP_EMAIL_TRIGGER_IN_USE` hint tells terminal users to unpublish `error.details.automationIds`, but the output never prints those IDs. `renderKit` shows only the error code, so users cannot tell which automations to unpublish.

Print the affected IDs, or give an explicit `--json` command that retrieves them.

### Issue 3
src/commands/app-emails/upsert.ts:114-116
`upsert` accepts `--input -` and `--source-file -` together, then reads stdin twice. The first read consumes the stream; the second cannot read a separate email source and may send empty content or fail with an unrelated error. Reject this combination before reading either input.

### Issue 4
src/commands/app-emails/kit.ts:undefined-135
After a partial kit failure, this message says to rerun with the same flags. If the user supplied `--idempotency-key`, that repeats the same key and body, which returns the original failed result for 24 hours rather than retrying the failed emails.

For a completed response with failed entries, tell users to omit or replace that key. Keep same-key retries for requests whose outcome is still unknown.

### Issue 5
src/commands/app-emails/kit.ts:155-156
The human kit output prints each snippet’s title and file name but drops `snippet.code`. A terminal user setting up Better Auth sees where code belongs but not the callbacks needed to send the emails. Print the code, or give an explicit `--json` command for retrieving it.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

### Issue 6
tests/commands/types.test.ts:605-616
The new registry test only checks generated text. It would still pass if the declarations stopped connecting to `brew.send` and no longer rejected unknown keys or wrong payloads. Add a compile test against the SDK with one valid call and expected errors for an unknown key and an invalid payload, so the advertised type checks remain covered after SDK updates.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds sending by trigger key, app-email setup commands, trigger key flags, and the generated SDK key registry.

  • Developers can send an email by its trigger key.
  • Developers can set up and check app emails by key.
  • Trigger keys now connect automation setup to typed send payloads.

Reviews (9) · Last reviewed commit: "chore(release): 0.19.0 after main's publ..." · Reviewed by Greptile

Comment thread src/commands/app-emails/upsert.ts
Comment thread src/commands/app-emails/kit.ts Outdated
Comment on lines +97 to +99
const base = await readJsonFlag(ctx, flags.input, '--input')
const sourcePath = flagString(flags.sourceFile)
const content = await readTextFlag(ctx, sourcePath, '--source-file')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Two flags consume stdin

upsert accepts --input - and --source-file - together, then reads stdin twice. The first read consumes the stream; the second cannot read a separate email source and may send empty content or fail with an unrelated error. Reject this combination before reading either input.

Knowledge Base Used: CLI input, output, and error behavior

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/app-emails/upsert.ts
Line: 97-99

Comment:
**Two flags consume stdin**

`upsert` accepts `--input -` and `--source-file -` together, then reads stdin twice. The first read consumes the stream; the second cannot read a separate email source and may send empty content or fail with an unrelated error. Reject this combination before reading either input.

**Knowledge Base Used:** [CLI input, output, and error behavior](https://app.greptile.com/brew-new/-/custom-context/knowledge-base/getbrew/brew-cli/-/docs/cli-input-output-and-errors.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment thread src/commands/app-emails/kit.ts Outdated
if (failed > 0) {
lines.push(
'',
`${failed} key${failed === 1 ? '' : 's'} failed. Run \`${prefix} app-emails kit\` again with the same flags to finish them.`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Recovery repeats failed results

After a partial kit failure, this message says to rerun with the same flags. If the user supplied --idempotency-key, that repeats the same key and body, which returns the original failed result for 24 hours rather than retrying the failed emails.

For a completed response with failed entries, tell users to omit or replace that key. Keep same-key retries for requests whose outcome is still unknown.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/app-emails/kit.ts
Line: 135

Comment:
**Recovery repeats failed results**

After a partial kit failure, this message says to rerun with the same flags. If the user supplied `--idempotency-key`, that repeats the same key and body, which returns the original failed result for 24 hours rather than retrying the failed emails.

For a completed response with failed entries, tell users to omit or replace that key. Keep same-key retries for requests whose outcome is still unknown.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +129 to +130
for (const snippet of result.stack.snippets) {
lines.push(`Snippet: ${snippet.title} (${snippet.file})`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Setup code is hidden

The human kit output prints each snippet’s title and file name but drops snippet.code. A terminal user setting up Better Auth sees where code belongs but not the callbacks needed to send the emails. Print the code, or give an explicit --json command for retrieving it.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/app-emails/kit.ts
Line: 129-130

Comment:
**Setup code is hidden**

The human kit output prints each snippet’s title and file name but drops `snippet.code`. A terminal user setting up Better Auth sees where code belongs but not the callbacks needed to send the emails. Print the code, or give an explicit `--json` command for retrieving it.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +432 to +443
expect(result.code).toBe(0)
const text = readFileSync(out, 'utf8')
expect(text).toContain(
[
"declare module '@brew.new/sdk' {",
' interface BrewTriggerKeyRegistry {',
' "auth.password-reset": PasswordResetPayload',
' "user.signup": UserSignedUpPayload',
' }',
'}',
'',
].join('\n')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Type checks lack compile coverage

The new registry test only checks generated text. It would still pass if the declarations stopped connecting to brew.send and no longer rejected unknown keys or wrong payloads. Add a compile test against the SDK with one valid call and expected errors for an unknown key and an invalid payload, so the advertised type checks remain covered after SDK updates.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/commands/types.test.ts
Line: 432-443

Comment:
**Type checks lack compile coverage**

The new registry test only checks generated text. It would still pass if the declarations stopped connecting to `brew.send` and no longer rejected unknown keys or wrong payloads. Add a compile test against the SDK with one valid call and expected errors for an unknown key and an invalid payload, so the advertised type checks remain covered after SDK updates.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +94 to +100
if (keys !== undefined && (sets !== undefined || stack !== undefined)) {
// The API takes `keys` instead of `sets` and `stack`, silently.
throw new CliUsageError(
'--keys names exact kit keys: pass it without --sets and --stack.'
)
}
const base = await readJsonFlag(ctx, flags.input, '--input')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Selection flags get ignored

With app-emails kit --sets lifecycle --input '{"keys":["auth.otp"]}' --yes, the conflict check sees only the flags and passes. mergeInput keeps keys, which takes precedence over sets, so the command publishes auth.otp instead of the lifecycle emails requested by the flag.

Check for conflicting selections after merging --input, before calling the SDK.

Knowledge Base Used: CLI input, output, and error behavior

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/app-emails/kit.ts
Line: 94-100

Comment:
**Selection flags get ignored**

With `app-emails kit --sets lifecycle --input '{"keys":["auth.otp"]}' --yes`, the conflict check sees only the flags and passes. `mergeInput` keeps `keys`, which takes precedence over `sets`, so the command publishes `auth.otp` instead of the lifecycle emails requested by the flag.

Check for conflicting selections after merging `--input`, before calling the SDK.

**Knowledge Base Used:** [CLI input, output, and error behavior](https://app.greptile.com/brew-new/-/custom-context/knowledge-base/getbrew/brew-cli/-/docs/cli-input-output-and-errors.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment thread src/commands/types.ts
@thedogwiththedataonit thedogwiththedataonit changed the title send by key, app-emails, trigger keys (C2) send by key, app-emails, trigger keys (C2, 0.17.0) Oct 16, 2026
thedogwiththedataonit added a commit that referenced this pull request Oct 16, 2026
`POST /v1/emails/import` gains `props` (`fold` | `merge_tags`),
`placeholders` (`resend`) and `variables`, and its response a `variables`
list. The bundled spec is #54's (brew-v2 claude/app-emails-setup
84cea75be5) plus exactly the delta of brew-v2
claude/import-merge-tag-modes 6be9d56b25 over its base, main 8c7020e535;
no other operation changes. Generated types regenerated. Re-vendor from
brew-v2 main once it merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 16, 2026
`POST /v1/emails/import` gains `props` (`fold` | `merge_tags`),
`placeholders` (`resend`) and `variables`, and its response a `variables`
list. The bundled spec is #54's (brew-v2 claude/app-emails-setup
84cea75be5) plus exactly the delta of brew-v2
claude/import-merge-tag-modes 6be9d56b25 over its base, main 8c7020e535;
no other operation changes. Generated types regenerated. Re-vendor from
brew-v2 main once it merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 16, 2026
`POST /v1/emails/import` gains `props` (`fold` | `merge_tags`),
`placeholders` (`resend`) and `variables`, and its response a `variables`
list. The bundled spec is #54's (brew-v2 claude/app-emails-setup
84cea75be5) plus exactly the delta of brew-v2
claude/import-merge-tag-modes 6be9d56b25 over its base, main 8c7020e535;
no other operation changes. Generated types regenerated. Re-vendor from
brew-v2 main once it merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 20, 2026
…red pager

main published 0.17.0, so #54 is 0.18.0 and this release (with the
launchers, synced) is 0.19.0. `init` writes the contracts file through the
`types` pager, which now walks main's cycle-safe `collectAll` with the
command context, and passes the command's stop signal to the contract
reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 20, 2026
`POST /v1/emails/import` gains `props` (`fold` | `merge_tags`),
`placeholders` (`resend`) and `variables`, and its response a `variables`
list. The bundled spec is #54's (brew-v2 claude/app-emails-setup
84cea75be5) plus exactly the delta of brew-v2
claude/import-merge-tag-modes 6be9d56b25 over its base, main 8c7020e535;
no other operation changes. Generated types regenerated. Re-vendor from
brew-v2 main once it merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thedogwiththedataonit thedogwiththedataonit changed the title send by key, app-emails, trigger keys (C2, 0.17.0) send by key, app-emails, trigger keys (C2, 0.18.0) Oct 20, 2026
thedogwiththedataonit added a commit that referenced this pull request Oct 21, 2026
…red pager

main published 0.17.0, so #54 is 0.18.0 and this release (with the
launchers, synced) is 0.19.0. `init` writes the contracts file through the
`types` pager, which now walks main's cycle-safe `collectAll` with the
command context, and passes the command's stop signal to the contract
reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 21, 2026
- A template or React Email file whose key's trigger already starts a
  published automation no setup made (409 APP_EMAIL_TRIGGER_IN_USE) is
  left `manual` with the API's message instead of failing the run: a
  re-run alone repeats the refusal, and the next run after the person
  unpublishes that automation sets it up (non-done items re-plan).
- The #2064 and #2145 spec commits fold into #54's vendored #2169 spec;
  CHANGELOG, RELEASING and comments name brew-v2 #2169 @ 6f8ef50544.
- Regenerated src/generated/skills.ts after the restack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 21, 2026
…arity

- The connection routes come with #54's vendored #2169 spec, so the
  `chore(spec)` commit for #2146/#2068 folded away and these commands
  empty the spec parity test's pending-build list.
- README, the brew-cli skill and CHANGELOG say what #2169's idempotent
  PUT connection guarantees: a re-run keeps `connectionId` and
  `webhookUrl`, replaces the secret in place, keeps the auth emails set up
  and finishes the failed ones.
- init --domain with a DKIM_SELECTOR_IN_USE warning offers neither the
  DNS records step nor Connect automatically (test).
- RELEASING, CHANGELOG and comments name brew-v2 #2169 (deployed) and the
  spec-drift sentinel's URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit and others added 3 commits October 25, 2026 22:26
The bundled spec is brew-v2 main (the spec CLI main already carries)
plus exactly the spec delta of brew-v2 claude/app-emails-setup
c72bb45467 (#2066, stacked on #2013 trigger keys, #2017 fire by key and
GET /v1/app-emails, GET|PUT /v1/app-emails/{triggerKey},
POST /v1/app-emails/kit, `key` / `appEmail` on trigger rows, and the
sandbox fields and error codes. Three-way merged against the stack's
merge base 4afd0668ee; every operation and schema matches one side or
carries both sides' changes. Generated types regenerated.

Re-vendor from brew-v2 main once those PRs merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `send <key>` binds `brew.send` (the by-key fire): `--to` sets
  payload.email over `--data <json|->`, `--idempotency-key`, destructive
  with a confirm summary naming key and recipient; no recipient exits 2.
  `automations.triggers.fireByKey` is a reviewed SDK skip (alias of send).
- `app-emails list|get` (sender + readiness table), `upsert <key>`
  (starter or --source-file with format from the extension, --no-publish
  skips the gate, --overwrite), `kit` (--sets/--stack/--keys/--domain/
  --overwrite; exits 1 when a key failed). Upsert and kit take the SDK's
  5-minute defaults as their deadline.
- `--key` on `automations triggers create|update`; KEY column in list.
- `types` appends a BrewTriggerKeyRegistry augmentation for keyed
  triggers (byte-identical output when none, pinned by a golden test;
  `--no-registry` opts out). The file builder and the trigger pager are
  exported for `init`.
- Dependency range `@brew.new/sdk ^12.3.0`; bun.lock is refreshed once
  12.3.0 is on npm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit and others added 8 commits October 25, 2026 22:26
`types` now emits the platform's TypeScript contract per trigger
(#58). The registry block reads each trigger's key from the
list row and the type name from its contract; the key-less output equals
`--no-registry` byte for byte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
package.json goes to 0.17.0 and the Unreleased notes (mcp install, browser
sign-in, send and app-emails, the canvas SDK methods) become 0.17.0.
RELEASING.md gains the app email SDK gate: `send` and `app-emails` need the
SDK release carrying GetBrew/typescript-sdk#55 and #64, which is not on npm
yet, so the dependency and bun.lock stay on main's pin until it publishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es confirm

Review against brew-v2#2066 (c72bb45467) and typescript-sdk#64:

- `upsert --input -` always confirms: stdin can set `overwrite` or
  `publish`, which the gate cannot read ahead (it could replace a design
  a teammate edited in Brew without --yes).
- `kit`: an empty `--keys`/`--sets` is a usage error instead of "the whole
  kit"; `--keys` with `--sets`/`--stack` is refused (the API takes keys
  instead of both, silently); the summary reads keys, sets, stack and
  overwrite passed inline through `--input`.
- The trigger key grammar, `--format`, `--sets`, `--stack` and kit keys
  are checked before any request, so a typo exits 2, not 1. A test pins
  the vocabularies to the vendored spec.
- `upsert` and `kit` say they need the `automations` and `emails` scopes;
  README notes `types --no-registry` for projects without the SDK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ted code

- RELEASING app email SDK gate and the 0.17.0 changelog name the SDK
  versions: #55 ships as 13.1.0 and #64 as 13.2.0, after the SDK's
  13.0.0; the CLI takes `^13.2.0`.
- `app-emails upsert` passes `--input` payloadSchema fields through as
  given, so `secret: true` on a one-time code or sign-in link reaches the
  API; a test pins it (typed against the vendored spec) and a new example
  shows it.
- A test pins `send`'s not-started line naming `DOMAIN_REQUIRED` (#2059),
  which SDK 13.2.0 types now that its spec includes #2059.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…18.0 notes

main's pagination guard takes the request's starting cursor so a resumed
walk never fetches its first page twice; every other `--all` list passes
it, and `app-emails list` now does too (added to the resumed-pagination
table). The release notes and SDK gate follow main: 0.17.0 and SDK 13.0.0
are published, so this release is 0.18.0 and the dependency stays on the
published ^13.0.0 until 13.2.0 is on npm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
…rning

- kit: a failed key's hint says what a re-run needs: a transactional
  --domain (or none) for APP_EMAIL_DOMAIN_PURPOSE_MISMATCH, unpublishing
  error.details.automationIds for APP_EMAIL_TRIGGER_IN_USE, --overwrite
  for APP_EMAIL_EDITED_IN_BREW; anything else is still "run it again".
- upsert: DRAFT_SAVED_NOT_LIVE reads "draft saved, the published version
  keeps sending" and keeps the `send` line.
- --domain help on both names the transactional/marketing refusal.
- MSW tests: APP_EMAIL_TRIGGER_IN_USE (details.automationIds),
  APP_EMAIL_DOMAIN_PURPOSE_MISMATCH, DRAFT_SAVED_NOT_LIVE, kit hints, and a
  DKIM_SELECTOR_IN_USE warning with suggestedNames on `domains add`.
- Skill, CHANGELOG and RELEASING name brew-v2 #2169 (deployed) as the API
  side and spec source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…/13.6.0

npm has CLI 0.18.0 (--lang, contact imports) and SDK 13.1.0-13.3.0 without
brew.send or appEmails; 13.4.0 is the SDK's pending email lang release. The
vendored spec is the brew-v2 #2169 @ 6f8ef50544 DX delta on main's spec
(brew-v2 87ac2c6242).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 25, 2026
…red pager

main published 0.17.0, so #54 is 0.18.0 and this release (with the
launchers, synced) is 0.19.0. `init` writes the contracts file through the
`types` pager, which now walks main's cycle-safe `collectAll` with the
command context, and passes the command's stop signal to the contract
reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 25, 2026
… ^13.6.0

npm has CLI 0.18.0 and SDK 13.1.0-13.3.0 without the DX features, so the
DX numbers move up: this PR is 0.20.0 (after #54's 0.19.0) and SDK_RANGE is
the adapters release, 13.6.0. A declared ^13.3.0 (published, no adapters)
now installs ^13.6.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 25, 2026
- A template or React Email file whose key's trigger already starts a
  published automation no setup made (409 APP_EMAIL_TRIGGER_IN_USE) is
  left `manual` with the API's message instead of failing the run: a
  re-run alone repeats the refusal, and the next run after the person
  unpublishes that automation sets it up (non-done items re-plan).
- The #2064 and #2145 spec commits fold into #54's vendored #2169 spec;
  CHANGELOG, RELEASING and comments name brew-v2 #2169 @ 6f8ef50544.
- Regenerated src/generated/skills.ts after the restack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 25, 2026
…ANGE ^13.6.0

After main's published 0.18.0 the DX numbers move up one: #54 0.19.0, #55
0.20.0, this PR 0.21.0. A declared ^13.3.0 or ^13.5.0 still gets the
install-sdk next step; ^13.6.0 does not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 25, 2026
…arity

- The connection routes come with #54's vendored #2169 spec, so the
  `chore(spec)` commit for #2146/#2068 folded away and these commands
  empty the spec parity test's pending-build list.
- README, the brew-cli skill and CHANGELOG say what #2169's idempotent
  PUT connection guarantees: a re-run keeps `connectionId` and
  `webhookUrl`, replaces the secret in place, keeps the auth emails set up
  and finishes the failed ones.
- init --domain with a DKIM_SELECTOR_IN_USE warning offers neither the
  DNS records step nor Connect automatically (test).
- RELEASING, CHANGELOG and comments name brew-v2 #2169 (deployed) and the
  spec-drift sentinel's URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thedogwiththedataonit added a commit that referenced this pull request Oct 25, 2026
After main's published 0.18.0 the DX numbers move up one (#54 0.19.0, #55
0.20.0, #61 0.21.0, this PR 0.22.0), and the SDK's DX releases follow its
published 13.3.0 and pending 13.4.0: adapters 13.6.0 (SDK_RANGE), connect
13.7.0. doctor --project's tests use those ranges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
const inUse = keysWith('APP_EMAIL_TRIGGER_IN_USE')
if (inUse.length > 0) {
hints.push(
`${inUse.join(', ')}: the trigger already starts a published automation no setup made, so every send would go out twice. Unpublish or delete the automations in error.details.automationIds, then run the kit again.`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Recovery hides the needed IDs

The APP_EMAIL_TRIGGER_IN_USE hint tells terminal users to unpublish error.details.automationIds, but the output never prints those IDs. renderKit shows only the error code, so users cannot tell which automations to unpublish.

Print the affected IDs, or give an explicit --json command that retrieves them.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/commands/app-emails/kit.ts
Line: 194

Comment:
**Recovery hides the needed IDs**

The `APP_EMAIL_TRIGGER_IN_USE` hint tells terminal users to unpublish `error.details.automationIds`, but the output never prints those IDs. `renderKit` shows only the error code, so users cannot tell which automations to unpublish.

Print the affected IDs, or give an explicit `--json` command that retrieves them.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex Fix in Cursor

@thedogwiththedataonit thedogwiththedataonit changed the title send by key, app-emails, trigger keys (C2, 0.18.0) send by key, app-emails, trigger keys (C2, 0.19.0) Oct 25, 2026
Sign up for free to 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.

1 participant