Skip to content

BEX-471 feat(json): emit snake_case twins for every camelCase --json key (transition step) - #106

Merged
piyushsarin-sib merged 2 commits into
features_set-dp-functionfrom
feat/json-output-snake-case-keys
Sep 4, 2026
Merged

BEX-471 feat(json): emit snake_case twins for every camelCase --json key (transition step)#106
piyushsarin-sib merged 2 commits into
features_set-dp-functionfrom
feat/json-output-snake-case-keys

Conversation

@piyushsarin-sib

Copy link
Copy Markdown
Collaborator

Summary

Step 1 of moving the CLI's machine-readable output to snake_case: every --json document now carries each camelCase key together with its snake_case twin. Nothing is removed, so every existing jq .appId keeps working. camelCase goes away in the next major (step 2, separate ticket).

Jira: BEX-471 (follow-up to BEX-470). Base branch: features_set-dp-function.

Why

--json output mixed two conventions. Pass-through commands emit the wire's snake_case (app listapp_id, app submitapp_id/form_url, function *); CLI-built objects emit camelCase (createappId/clientId/redirectUri, uploadupToDate, install/uninstallaccountId, whoamiorganizationId, the error envelope → exitCode/statusCode). The smoke suite's own pickId helper accepts three spellings because it cannot predict which one a command uses. app-config.json settled on snake_case in #104; scripts should get one spelling too — but a hard rename is a breaking change the CLI cannot migrate for the user, hence the dual-key transition.

What changed

  • src/lib/json-output.ts: withSnakeCaseAliases() runs inside jsonOutput(). For each camelCase key at the top level of the document (per element for an array document) and inside the error envelope, a snake_case twin is inserted right after it. A twin never overwrites a key the object already has.
  • Two explicit overrides map to the wire name rather than the mechanical conversion: redirectUri (app create) and redirectUris (app credentials) → redirect_uris.
  • Shallow on purpose. Nested objects are not rewritten: ui_app blocks, the upload diff's current/next, Function records and contact data are either already wire-shaped or the user's own data, and rewriting keys inside them would change their meaning.
  • No per-command edits. All 36 jsonOutput call sites get the behaviour from the one function.

Examples from the built binary:

$ brevo app delete --app-id … --force --json
{"deleted":true,"appId":"…","app_id":"…"}

$ brevo app list --json        # unauthenticated → envelope
{"error":{"name":"CliError","message":"Not authenticated. Run: brevo login","exitCode":1,"exit_code":1}}

Docs

  • agent-context/SKILL.md (Hard rules) and agent-context/AGENTS.md (Conventions): new rule — both spellings are present, prefer snake_case, camelCase is deprecated and removed in the next major. JSON-errors sections and the scaffoldSkipped mention updated to show both keys.
  • Changeset: appended to the branch's pending .changeset/function-app-type-registry.md (minor).
  • Public docs (CLI reference --json examples, JSON errors section) need the same note; flagged in BEX-471 for product/docs.

Tests

  • json-output.test.ts: new withSnakeCaseAliases suite — ordering, no-overwrite, redirect_uris override, multi-hump keys, shallow behaviour, array documents, error envelope, primitives, and that jsonOutput writes the aliased form. emitJsonError expectation updated.
  • The seven command tests that pinned an exact camelCase shape (delete, withdraw, install ×2, uninstall ×2) now pin both spellings.
  • Smoke scripts untouched: pickId and the created.appName / created.redirectUri assertions keep passing; they switch to snake_case-only in step 2.

Not in this PR (step 2, next major)

Removing the camelCase keys, tightening the smoke helper to snake_case only, and a breaking-change changelog entry.

Checks

  • yarn lint
  • yarn test:ci ✅ (70 suites, 1600 tests)
  • yarn build
  • yarn format:check

🤖 Generated with Claude Code

piyushsarin-sib and others added 2 commits September 4, 2026 16:19
…nsition step)

Machine-readable output had two conventions: pass-through commands
(app list, app submit, function *) emit the wire's snake_case, while
CLI-built objects (create, upload, credentials, install, delete, whoami,
the error envelope) emit camelCase. app-config.json settled on
snake_case in BEX-470, and scripts should be able to read one spelling
everywhere.

Renaming outright would break every `jq .appId` in a pipeline with
nothing the CLI could migrate, so this is the deprecation step:
jsonOutput() now adds a snake_case twin after each camelCase key at the
top level of every document (per element for arrays) and inside the
error envelope. redirectUri / redirectUris alias to the wire name
redirect_uris. Nested objects are left untouched — they are wire records
or user data. Nothing is removed; camelCase goes away in the next major.

Agent docs and the changeset carry the deprecation note; the seven tests
that pinned exact camelCase shapes now pin both spellings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ase-keys

Resolve the agent-doc conflicts by taking the base's copies (which add
the Brevo Function sections and the three-app-type wording) and
re-applying this branch's three edits on top: the snake_case --json
key rule, the exit_code / status_code lines in the JSON-errors
sections, and the scaffold_skipped mention.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@piyushsarin-sib
piyushsarin-sib merged commit 032828a into features_set-dp-function Sep 4, 2026
@piyushsarin-sib
piyushsarin-sib deleted the feat/json-output-snake-case-keys branch September 4, 2026 10:54
@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

piyushsarin-sib added a commit that referenced this pull request Sep 4, 2026
* feat: add Brevo Function commands with smoke tests

Add `brevo function list` and `brevo function get <id>` commands for
managing Brevo Functions, plus the "Brevo Function" app type choice
in `brevo app create`.

- New service (src/services/function.ts) with list, draft list, and get
- New command handlers (src/commands/function/list.ts, get.ts)
- Register functionCommandGroup in definitions.ts and bin/index.ts
- Add Function commands section to root help screen
- Add brevo_function template flag for app-config.json rendering
- Add Brevo Function app type to interactive create prompt (private only)
- Unit tests for service, list command, and get command (23 tests)
- Command registration tests in definitions.test.ts (4 tests)
- Help formatting tests updated for functionCommandGroup
- New smoke suite (scripts/smoke/function.ts) exercising list, list
  --draft, get, and get-not-found against a real account — opt-in via
  `yarn smoke --suite=function`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: gate Brevo Function behind __BREVO_PREVIEW__ and add fn alias

- Add `brevo-function-type` to FEATURE_STAGE as a preview feature
- Move function group definition to preview-definitions.ts so esbuild
  can eliminate it from published builds
- Gate the Brevo Function choice in `app create` behind __BREVO_PREVIEW__
- Gate the Function commands section in help.ts behind __BREVO_PREVIEW__
- Add `aliases` support to SubcommandGroupDefinition so `brevo fn list`
  and `brevo fn get` work as shortcuts
- Update tests for the conditional export and gated app-type prompt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add brevo function activate, deactivate and delete commands

Add three management commands to the Brevo Function group:
- `brevo function activate <id>` — PATCH with is_active: true
- `brevo function deactivate <id>` — PATCH with is_active: false
- `brevo function delete <id>` — DELETE with --force to skip confirmation

All three are gated behind __BREVO_PREVIEW__ alongside the existing
list and get commands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add brevo fn init command with AI generation, template flow, and SSE error handling

- Add `brevo fn init` command with two creation flows: AI generation and global templates
- Implement SSE streaming for AI code generation and iteration with colored progress stages
- Add preview execution (fetch contacts + execute + results table) after generation and iteration
- Auto-derive `attribute_id` from function name in SCREAMING_SNAKE_CASE
- Add graceful error handling for SSE stream termination and API failures
- Add shared function app selector (`select-function.ts`) used by fn subcommands
- Update all fn subcommands to use shared app selector
- Set `source: 'cli'` on all dp-functions API calls
- Wrap SSE reader.read() errors into ApiError for clean failure messages
- Add comprehensive tests for init command and SSE stream module

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use draft_id instead of code for post-iterate execute preview

The backend updates the draft in the database during iterate (PATCH
/generate/stream) in all auth modes, so referencing the draft by ID
is consistent with the initial preview and avoids sending the full
code payload to the execute endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: show friendly message when Brevo Functions is not enabled (403)

The dp-functions backend returns 403 with code `feature_not_enabled`
when the account lacks the dp-functions entitlement. Map this to a
user-friendly message in both the REST client (ApiClient) and the
SSE stream handler so all fn subcommands display:

  "Brevo Functions is not enabled for this account."

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: address SonarCloud issues — reduce complexity, fix nested ternaries

- sse-stream.ts: extract performSSEFetch, handleSSEErrorResponse, readChunk,
  processSSELine, flushSSEState helpers to reduce cognitive complexity from 49
  to ~11; replace nested ternary with extractErrorMessage function
- init.ts: extract executePreview, saveGeneratedFunction, mergeGenerateResult,
  updateSpinnerFromEvent, accumulateResult helpers to reduce cognitive complexity
  of processGenerateStream (38→~8) and aiGenerationFlow (40→~11); combine
  consecutive push calls into single push with multiple args
- list.ts: extract inner template literals to variables to eliminate nesting
- templates/index.ts: replace nested ternary with if/else in resolveTemplateFlags
- create.ts: replace nested ternary with if/else in buildCreatePayload; extract
  cacheCreatedAppCredentials to reduce createCommand complexity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: address remaining SonarCloud warnings

- Parameterize similar tests in sse-stream.test.ts and definitions.test.ts (S5976)
- Add formatCellValue helper for safe object stringification in init.ts (S6551)
- Replace boolean statusBadge function with ACTIVE_BADGE/INACTIVE_BADGE constants in list.ts (S2301)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: fix last S6551 warning in formatCellValue (init.ts)

Use explicit type checks (string, number, boolean) instead of
String(value) on unknown, so SonarCloud can verify no object
reaches the default toString path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: hide attribute_id from predefined template preview table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove Attribute ID line from template preview output

The Attribute ID was shown both as a header line and could appear in the
results table. Remove the explicit header line so the preview only shows
the description and the data table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR #69 review comments

- Use shared functionService from container instead of local instance (init.ts)
- Wire STAGE_LABELS to lang file keys instead of hardcoded strings
- Rethrow ApiError/CliError in generate and iterate catch blocks
- Guard iterate request and preview against missing draftId
- Match duplicate-name detection on HTTP 409 status code, not message copy
- Union columns across all rows in printResultsTable
- Document AbortSignal.timeout as a deadline in sse-stream.ts
- Evaluate statusBadge at call time to respect TTY/NO_COLOR (list.ts)
- Change --id [id] to --id <id> in all fn subcommands
- Remove orphaned lang keys (stage labels, success messages, file-written)
- Hoist distribution_type out of template branches (app-config.json.tmpl)
- Remove unused src/__tests__/tsconfig.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve SonarCloud issues — cognitive complexity and boolean param

- init.ts: extract `runIterateRound` and `tryPreview` from
  `aiGenerationFlow` to reduce cognitive complexity from 21 to
  within the 15 threshold (typescript:S3776).
- list.ts: replace `statusBadge(isActive)` boolean-param function
  with two named functions `activeBadge()` / `inactiveBadge()`
  (typescript:S2301).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add `brevo fn deploy` command for deploying draft functions

Add a standalone deploy command that lets users deploy draft Brevo
Functions independently of the `brevo fn init` AI generation flow.

- New `brevo function deploy` command with `--id` and `--json` flags
- Interactive draft picker when `--id` is omitted
- Preview with sample contacts before deployment
- Name prompt with duplicate-name (409) retry loop
- Stop flow with "Unable to deploy function" when preview returns errors
- Extract shared preview-table utilities from init.ts into preview-table.ts
- 11 test cases covering success, JSON, picker, errors, and edge cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve SonarCloud issues in deploy.ts — cognitive complexity and boolean param

- Extract `deployJsonMode`, `deployInteractive`, and `tryPreview` from
  the main handler to reduce cognitive complexity (typescript:S3776).
- Replace `assertDraftSelectionAllowed(jsonMode?)` boolean-param function
  with `assertInteractiveTerminal()` that only checks TTY (typescript:S2301).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: replace backtracking regex with lastIndexOf in deriveNameFromDescription

Sonar flagged `/\s+\S*$/` as super-linear due to backtracking.
Use `lastIndexOf(' ', 50)` instead — same word-boundary cut, O(n).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: link deployed function to app after creation

Add app-linking step to `function deploy` and `function init` flows.
After a function is created, it is linked to the selected app via
POST /v3/app-store/app-functions. Adds --app-id flag to deploy for
non-interactive use, and an interactive app picker when omitted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR #85 review — shared module, JSON safety, tests

- Extract selectFunctionApp/tryLinkFunctionToApp into select-app.ts,
  used by both deploy.ts and init.ts (review point 3)
- Fix --json output corruption: link runs silent, spinner uses
  { silent: true }, no logInfo to stdout (point 1)
- Add linked/app_id to JSON payload so callers can detect link
  status (point 2)
- Move app picker after preview+confirm to avoid wasted round-trips
  on early failure (point 4)
- Remove appId! non-null assertion — narrowed via control flow (point 5)
- Log link error detail behind isDebug via logDebug (point 6)
- Add 5 new tests: --app-id path, --json without --app-id, link
  failure in JSON mode, no-apps throw, link failure warning (point 7)
- Fix template literal + concatenation mix in app name display (point 8)
- Add changeset for new --app-id flag (point 8)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract shared deploy helpers to reduce code duplication

Move isDuplicateNameError, executePreview, tryPreview, and the
name-confirm-deploy loop into deploy-helpers.ts. Both deploy.ts
and init.ts now delegate to these shared functions, parameterized
by message constants. This addresses the SonarCloud duplication
quality gate (11.2% → well below 3%).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: remove changeset file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve SonarCloud duplication and nested template literal

- Generalize executePreview to accept templateArgs (draft_id or
  template_id) so init.ts templateFlow uses the shared function
  instead of an inline duplicate.
- Fix nested template literal in select-app.ts (S4624).
- Extract test setup into setupHappyPath() helper to reduce
  duplicated mock boilerplate across 16 tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: remove preview gate from brevo function commands (GA)

Move the `brevo function` surface out of the `__BREVO_PREVIEW__` build
gate so it ships in every published build. This follows the same
pattern as the UI-apps GA transition (BEX-290).

Changes:
- Flip FEATURE_STAGE['brevo-function-type'] from 'preview' to 'ga'
- Move function group definition from preview-definitions.ts to
  definitions.ts (always-defined, no longer conditional)
- Add function handler imports directly in definitions.ts
- Remove __BREVO_PREVIEW__ guard from bin/index.ts, help.ts, create.ts
- Move all FUNCTION_* strings from preview-messages.ts to en.ts
- Add GA markers (listFunctionCommand, initFunctionCommand,
  deployFunctionCommand) to scripts/build.mjs
- Update tests to reflect the GA state

Verified: yarn test (1513 pass), yarn build (public), PREVIEW=1 yarn
build (preview), yarn lint, yarn format:check — all clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add function suite to smoke test live surfaces

Include `function` in the smoke test's default and pinned suites so the
GA'd Brevo Function commands are exercised on every publish and pre-merge
lane — matching the CLAUDE.md requirement that a GA'd feature joins the
smoke test in the same PR.

- scripts/smoke-test.ts: add `function` to DEFAULT_SUITES
- smoke.yml: update workflow_call/dispatch defaults and options
- smoke-post-merge.yml: widen the pinned suite to `private,ui,function`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: expand function smoke suite with mutation steps, init flow, and dedicated account

Wire BREVO_TEST_API_KEY_FUNCTION secret and split function suite into its
own CI job so it runs against a dedicated dp-functions account. Add
activate/deactivate cycle, not-found error probes, deploy+cleanup, and
pty-driven function init (template path) with automatic cleanup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve SonarCloud duplication in function smoke not-found probes

Extract shared assertNotFound() helper from the four identical
not-found error probe functions, bringing new duplicated lines
density well under the 3% quality gate threshold.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve SonarCloud duplication in function action commands

Extract shared function state-change pattern (ID resolution, spinner,
404 handling, JSON/card output) into buildFunctionActionCommand helper.
Activate and deactivate become thin config wrappers; delete delegates
execution while keeping its confirmation prompt. Shared logic is tested
once in function-action.test.ts, individual tests verify wiring only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: reduce SonarCloud duplication across function commands and smoke tests

Extract resolveFunctionId and withNotFoundHandling helpers for get.ts,
deduplicate smoke test cleanup with deleteAndAssert and toggle helpers,
and remove unused import flagged by code-quality bot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add app_type field to app-config.json template

Add explicit `app_type` field ('oauth', 'ui', or 'function') to the
scaffolded app-config.json so the app type is immediately visible
without relying solely on the presence of discriminator blocks.

Addresses PR #100 review comment by @piyush.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: address PR #100 review comments

- Add `function` to app-type registry with positive `brevo_function`
  detection on both config and record paths
- Add `function` row to capability matrix (no OAuth flow, no redirect
  URIs, no account-install)
- Add `brevo_function` field to `OAuthApp` type and `AppRecordLike`
- Add `brevo function deploy` to help section
- Replace IIFE with `resolveFunctionId` in `executeFunctionAction`
- Add pagination to `fetchFunctionList` for >50 functions
- Wire `ensureFresh` into `SSEStreamDeps` for token refresh before
  SSE connections
- Expose `runEnsureFresh()` on `ApiClient`, plumb through container
- Fix comment in templates/index.ts (`brevo_function` is mutually
  exclusive with oauth, not orthogonal)
- Add explanatory comments in init.ts, tsconfig.eslint.json, help.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add backward compatibility and smoke tests for app_type field

- Config backward compat: legacy configs without app_type parse
  correctly (field is undefined, not an error)
- Config round-trip: app_type survives writeProjectConfig, and legacy
  configs without it stay absent after write-back
- Wire isolation: app_type never leaks into the upload payload
  (UploadAppPayload has no app_type field)
- Template rendering: brevo_function branch renders app_type correctly,
  all three app types produce the right value
- Capability matrix: function type has no OAuth/UI capabilities, only
  review-lifecycle on public distribution
- Record resolution: brevo_function record resolves to function type;
  blockless records still resolve to UI (not function)
- Recoverability: function apps are always recoverable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add changeset for function app-type registry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: rename app_type to appType in config for camelCase consistency

Aligns with the existing camelCase convention used by other config
fields (appId, appName, logoUri). The field is local metadata only
and never sent to the server, so this is a config-shape change with
no wire impact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update stale doc comments for appType and add app_type migration

- Fix doc comment in config.ts that said "there is no separate appType key"
- Fix comment in en.ts that said "there is no app-type field"
- Fix AGENTS.md.tmpl that said "There is no appType key"
- Add legacy app_type → appType migration in readProjectConfigAt
- Add 3 migration tests (read migration, write-back cleanup, precedence)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: revert appType back to app_type in config

Rename the informational app-type field from camelCase `appType` back to
snake_case `app_type` to match the wire-mirrored key convention used by
other platform-facing fields (`distribution_type`, `ui_app`,
`brevo_function`). The migration logic now migrates the legacy `appType`
key to `app_type` on read and drops it on write-back.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* BEX-470 refactor(config): normalize app-config.json keys to snake_case with legacy migration (#104)

* refactor(config): normalize app-config.json keys to snake_case with legacy migration

Rename the camelCase keys in app-config.json to snake_case, matching the
wire contract: appId → app_id, appName → app_name, logoUri → logo_uri,
appType → app_type, auth.redirectUris → auth.redirect_uris. The
ProjectConfig interface now IS the file shape, so every read site was
found by the compiler.

Backward compatibility: readProjectConfigAt folds the legacy camelCase
keys (and the older auth.redirectUrls) through one alias table, drops
them from the returned object, and warns once on stderr when a file
carries both spellings with different values.

Migration on write: create/upload/scaffold/start now emit snake_case
only. upload and scaffold also rewrite an in-sync legacy file on their
no-op paths via migrateProjectConfigKeys(), so one run migrates a
project regardless of drift. Values are never changed.

Also: template, scaffold docs, agent-context docs, README, smoke scripts
(accept both spellings so published-build runs still pass), tests for
camelCase-only / snake_case-only / mixed reads and the write-back
migration, and a changeset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(config): announce key migration on every rewrite and add agent migration note

Print the snake_case migration notice whenever a legacy app-config.json is
rewritten — after an upload push and after a consented scaffold refresh,
not only on the no-op paths — and word it so users know to update their
own scripts that read the file. Add an explicit migration rule to
agent-context/SKILL.md and AGENTS.md so agents steer custom code to the
new key names and never write the camelCase names.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(upload): assert the block error pre-empts the app_type label check

Pin the ordering `assertAppTypeAgrees` documents: a config that is BOTH
mislabelled and structurally broken must report the structural problem, so a
partner is never sent off to fix a label only to hit the real refusal on the
next upload. Asserted positively (the entry-named `.label:` error) plus an
explicit check that the message does not mention `app_type`, rather than via
`rejects.not.toThrow`, which can pass for the wrong reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: describe app_type as a validated label, not a discriminator

`app upload` now reads `app_type` to check it agrees with the blocks, so three
docs that said the CLI never reads it were wrong as written.

- CLAUDE.md: read for validation, never for detection; the pre-flight ordering
  and why it is last; and the wire decision, which was nowhere stated — the
  field is file-only because `UploadAppPayload` and the create body are closed
  structs built key by key, so nothing strips it and bo-be needs no change.
- The scaffolded AGENTS.md template said "the CLI never reads it". It now says
  what the field is for and that a hand-edited block needs the label updated.
- agent-context/SKILL.md and AGENTS.md documented the key but not its meaning;
  both now carry the same paragraph, kept in sync per the repo rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore(changeset): fold the snake_case entry into the branch's single changeset

CLAUDE.md asks for one changeset file per branch, appended to rather than
multiplied. Merges the separate snake_case file into the existing one and adds
the app_type agreement check. Bump level stays `minor` — both entries already
warranted it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

* fix: address PR #100 review comments for brevo function

- Add Brevo Functions documentation to SKILL.md and AGENTS.md (was
  missing entirely — agents had no knowledge of the function commands)
- Fix `brevo app upload` for Function apps: skip redirect_uris check,
  send `brevo_function: {}` on the wire, omit `auth` block, handle
  write-back and diff rendering correctly for the third app type
- Paginate `fetchDraftFunctionList` (was capped at 50, causing silent
  truncation and false not-found errors in `brevo fn deploy --id`)
- Fix `fetchFunctionList` pagination loop: use `has_more` + empty-page
  guard instead of `total` comparison to prevent infinite loops
- Refactor `executeFunctionAction` to delegate to `withNotFoundHandling`
  instead of re-implementing the 404 catch block (Sonar duplication)
- Move hardcoded strings (`Description:`, `Name:`, `Untitled Function`)
  to `src/lang/en.ts`
- Remove unused `functionService` mock from function-action.test.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* BEX-471 feat(json): emit snake_case twins for every camelCase --json key (transition step) (#106)

feat(json): emit snake_case twins for every camelCase --json key (transition step)

Machine-readable output had two conventions: pass-through commands
(app list, app submit, function *) emit the wire's snake_case, while
CLI-built objects (create, upload, credentials, install, delete, whoami,
the error envelope) emit camelCase. app-config.json settled on
snake_case in BEX-470, and scripts should be able to read one spelling
everywhere.

Renaming outright would break every `jq .appId` in a pipeline with
nothing the CLI could migrate, so this is the deprecation step:
jsonOutput() now adds a snake_case twin after each camelCase key at the
top level of every document (per element for arrays) and inside the
error envelope. redirectUri / redirectUris alias to the wire name
redirect_uris. Nested objects are left untouched — they are wire records
or user data. Nothing is removed; camelCase goes away in the next major.

Agent docs and the changeset carry the deprecation note; the seven tests
that pinned exact camelCase shapes now pin both spellings.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Piyush Sarin <piyush.sarin@brevo.com>
Co-authored-by: Piyush Sarin <84779634+piyushsarin-sib@users.noreply.github.com>
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.

2 participants