Repository navigation
send by key, app-emails, trigger keys (C2, 0.19.0) - #54
thedogwiththedataonit wants to merge 11 commits into
Conversation
| const base = await readJsonFlag(ctx, flags.input, '--input') | ||
| const sourcePath = flagString(flags.sourceFile) | ||
| const content = await readTextFlag(ctx, sourcePath, '--source-file') |
There was a problem hiding this comment.
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.| if (failed > 0) { | ||
| lines.push( | ||
| '', | ||
| `${failed} key${failed === 1 ? '' : 's'} failed. Run \`${prefix} app-emails kit\` again with the same flags to finish them.` |
There was a problem hiding this 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.
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.| for (const snippet of result.stack.snippets) { | ||
| lines.push(`Snippet: ${snippet.title} (${snippet.file})`) |
There was a problem hiding this comment.
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!
| 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') |
There was a problem hiding this 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.
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!
dcf2763 to
cf16a99
Compare
0ef5838 to
9a731ff
Compare
cf16a99 to
b962031
Compare
9a731ff to
4c151ea
Compare
| 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') |
There was a problem hiding this comment.
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.b962031 to
d23d42d
Compare
4c151ea to
2ff44ff
Compare
`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>
`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>
282dd6c to
4f45e49
Compare
`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>
d23d42d to
fc46dbd
Compare
4f45e49 to
d4b5b60
Compare
…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>
`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>
fc46dbd to
7fcc8af
Compare
d4b5b60 to
b3ce2c5
Compare
…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>
- 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>
…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>
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>
`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>
7fcc8af to
a3f81fb
Compare
b3ce2c5 to
c00be72
Compare
…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>
… ^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>
- 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>
…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>
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.` |
There was a problem hiding this comment.
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.
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.
Publish order (nothing here is published, merged or tagged)
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).bun add @brew.new/sdk@^<that version>(regeneratesbun.lockfrom the registry, replacing main's git pin), re-vendoropenapi/public-api-v1.yamlfrom brew-v2 main (once #2169 merges), frozen install + full checks, then CLI 0.17.0 → 0.18.0 → 0.19.0.brew.newandcreate-brewon 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).--tosetspayload.email(wins over--data),--data <json|->,--idempotency-key(route isfail_closed). Destructive (exit 4 without--yes).automations.triggers.fireByKey→SDK_SKIP_LIST(alias ofsend).app-emails list | get <key> | upsert <key> | kitbindGET /v1/app-emails,GET|PUT /v1/app-emails/{triggerKey},POST /v1/app-emails/kit.upsert(starter or--source-filewith--formathtml/mjml/jsx,--overwrite,--no-publish) andkit(--sets,--stack,--keys,--domain,--overwrite,--idempotency-key) are destructive because they publish;kitexits 1 when a key failed. Both need theautomationsandemailsscopes (stated in help, README and the skill).--keyonautomations triggers create|update;KEYcolumn intriggers list.typesappends theBrewTriggerKeyRegistryaugmentation for keyed triggers on top of main's contract-artifacttypes(brew-cli#58); a key-less workspace gets the same bytes as--no-registry.listAllTriggers,fetchContractArtifactsandbuildContractsFileare exported forinit.claude/app-emails-setup84cea75be5exactly (CLI main's spec is brew-v2 main8c7020e535byte for byte; #2066 sits on #2013e499465757, #201754a862fee0and #20594792d4a323), so it carriessecreton custompayloadSchemafields. Generated types regenerated.app-emails upsertpasses--inputfields (incl.secret: true) through, pinned by a test and an example; a test pinssend's not-started line namingDOMAIN_REQUIRED, which SDK 13.2.0 types (its spec now includes #2059).## 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
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'ssdk.*namespace), generated docs.upsert --input -always confirms (stdin could setoverwrite);kitrejects an empty--keys/--sets(was "whole kit") and--keyswith--sets/--stack, and its summary reads inline--input; the trigger key grammar,--format,--sets,--stackand 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✓ ·test59 files / 824 tests ✓ (parity-sdk + parity-spec green) · generated types fresh ✓ ·docs:commands:check✓ ·generate:skills:check✓. CI runs only on PRs intomain.Live validation owed (after deploy + publish)
sendto an org member in sandbox and to a non-member (DOMAIN_REQUIRED),app-emails kit --stack better-auth,upsertre-run and409 APP_EMAIL_EDITED_IN_BREW,typeson a brand with kit keys.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Do not merge yet: mixed kit selections can still publish different emails than the flags request.
Fix with agent prompt
Summary
Adds sending by trigger key, app-email setup commands, trigger key flags, and the generated SDK key registry.
Reviews (9) · Last reviewed commit: "chore(release): 0.19.0 after main's publ..." · Reviewed by Greptile