Skip to content Documentation index for agents (llms.txt)
Glotto Beta
Get started

Custom code

Generated SDKs cover the API, but real projects need a little hand-written code too — a convenience wrapper, an extra helper, a tweak to a generated method. Glotto is built so your code survives regeneration. There are two mechanisms:

  1. The customer-owned extension directory — lib/ in most SDKs and Sources/<Module>/Custom/ in Swift. Shipped today.
  2. Patch preservation — three-way merge of edits to generated files, in the release PR and (opt-in) locally.

For anything you write from scratch, prefer that extension directory: it’s where preservation is guaranteed.

The customer-owned extension directory

Every generated SDK has an extension directory that is yours. Most engines use lib/; Swift uses Sources/<Module>/Custom/, inside its conventional SwiftPM target. The contract is enforced by glotto generate and drift detection the same way for every language:

  • Customer-owned, write-if-absent. Glotto seeds the directory once with an entry-point stub, then never overwrites that customer-owned file. Every managed file is regenerated normally, but your extension is left exactly as you left it.
  • Excluded from drift. Drift detection ignores customer-owned files on both sides, so an edit there never shows up as drift or blocks a release — the extension is not part of the “regenerate must match” contract.
  • A stable import target. The entry-point stub (TypeScript’s lib/index.ts, Python’s lib/__init__.py, Swift’s Sources/<Module>/Custom/Custom.swift, …) gives you a fixed place to export from, and its header comment tells you how code you add there reaches the SDK’s surface in that language.
The half of the tree Glotto never writes twice One TypeScript target, and one real generate run over it. The stub below is the file Glotto writes once and then never writes again; the method beside it is regenerated on every run, under the checksum that decides which of the two you are editing. Derived from examples/custom-code-lib/inputs — every byte below is sliced from that demo or from one real generate run over it.

Your glotto.yml — a TypeScript target with a customer dependency

examples/custom-code-lib/inputs/glotto.yml targets
targets:
  typescript:
    dependencies:
      decimal.js: "10.6.0"

What Glotto seeds ONCE — src/lib/index.ts, yours from then on

typescript/src/lib/index.ts whole file

// Custom code — never overwritten by `glotto generate`.
//
// This file, and everything else under `lib/`, is yours. `glotto generate`
// writes it once and then leaves it untouched on every subsequent run, and
// drift detection ignores `lib/` entirely — so helpers, overrides, and
// extensions you add here survive regeneration. Re-export them from this entry
// point to keep a single import surface for your custom code.

export {};
sha256 066b46389fdd95f09343610f9fdb02d97e8c50f27626cd164f919fe40f029ab8

What Glotto owns — regenerated every run, under the checksum that guards your edits

typescript/src/resources/widgets.ts WidgetsResource.createWidget

  createWidget(params: NewWidget, options?: RequestOptions): Promise<Widget> {
    return this.core.request<Widget>('POST', '/widgets', { ...options, body: params, contentType: 'application/json', responseBodyRequired: true });
  }
generated-checksum b38a4b66b0dff0f637973fcd8dab7be726b467bb46bb84528340f9c0fff86b27

Put helpers, wrappers, and hand-authored code in the extension directory, and it is preserved on every regenerate. Anything you expose from the stub reaches the SDK’s public surface according to that language’s normal package conventions.

Upgrading an existing Swift SDK: if Glotto finds the former lib/Custom.swift and no file at Sources/<Module>/Custom/Custom.swift, the next glotto generate copies its contents exactly to the SwiftPM target and retains the legacy file as a backup for you to remove after review. If both files already exist, Glotto leaves both untouched and prints the paths to reconcile; it never chooses one customer-owned copy over the other.

Every language, on the SDK’s surface: the lib/ machinery (write-if-absent + drift exclusion) is engine-agnostic, every SDK language seeds an entry-point stub, and custom code joins the SDK’s public surface per language: TypeScript and React Native re-export it as lib; Swift compiles Custom/ as part of its module; Java, Kotlin, Rust, C#, Dart, and Elixir compile lib/ into the SDK artifact; Ruby loads it with require "glotto"; and the Go, Python, and PHP stubs document the exact import path to use.

Dependencies for your extension

Declare runtime dependencies in targets.<language>.dependencies in glotto.yml. The configuration pane above declares decimal.js for the TypeScript extension. Every regeneration includes it in package.json, even when generated API code does not use it.

For that example, add a helper to src/lib/index.ts that imports { Decimal } from decimal.js and returns new Decimal(left).plus(right).toString() from a preciseTotal(left: number, right: number) function. Run these commands in the generated TypeScript package:

npm install
npm run build
node --input-type=module -e "import { lib } from './dist/index.js'; console.log(lib.preciseTotal(0.1, 0.2));"

The result is 0.3. After changing the API, run glotto generate from the project directory and build again. Your helper remains in place and its dependency remains declared. Add or remove dependencies in the configuration map; the next generation updates the manifest.

Target Coordinate example Requirement examples
TypeScript, React Native @acme/helper ^1.2.3, >=1.2.3 <2
Python python-dateutil >=2.9,<3, ==2.9.0
Go github.com/google/uuid v1.6.0
Rust itertools 0.14, >=0.14, <0.15
Java, Kotlin org.apache.commons:commons-lang3 3.18.0, [3.18,4)
C# Humanizer 2.14.1, [2.14.1,3.0.0)
PHP psr/log ^3.0, >=3.0 <4
Ruby concurrent-ruby ~> 1.3, >=1.3, <2
Elixir decimal ~> 2.3, >=2.3.0 and <3.0.0
Dart collection ^1.19.0, >=1.19.0 <2.0.0
Swift https://github.com/apple/swift-collections.git#Collections from: 1.1.0, 1.1.0, 1.1.0..<2.0.0

Quote version values in YAML. Entries declare registry package versions; source directories, Git branches, executable expressions and package-manager option objects are not version values. Swift requires the product after #: Glotto declares the package and links that product to the SDK target. Multiple products from one URL must use the same requirement. Kotlin multiplatform adds custom dependencies to commonMain, where its customer extension is compiled, so a configured coordinate must be a Kotlin Multiplatform artifact (one with an iOS variant), not a JVM-only one.

Generated requirements retain their feature flags and platform conditions. An identical requirement is accepted; a different requirement for the same package produces a configuration-path error. Requirements are compared as written, so use the generated spelling even when another expression would select equivalent versions. Python and NuGet package names are normalized before conflict checks. Package managers still resolve and build the resulting dependency graph.

Editing generated files

Sometimes you need to change a generated file directly. Glotto’s rule is simple: it never silently overwrites your edits.

Every generated file carries a checksum comment over the bytes Glotto produced — the generated-checksum stamped under the emitted pane above is the real one for that file, read out of the run that produced it rather than written here.

On the next regenerate, Glotto compares that checksum to the file’s current contents. If they match, the file is pristine and is regenerated freely. If they differ, you’ve hand-edited it — so Glotto performs a three-way merge (your version against the old and new generator output) and, on any conflict, writes standard conflict markers and surfaces them in the release pull request for you to resolve, rather than clobbering your change.

How it runs today: the checksum marker ships on every generated SDK language, the release-PR flow does the full three-way merge automatically, and locally glotto generate preserves your edited file untouched — or truly merges it when you opt in with --merge. Even the generated README.md is covered — its checksum rides in an HTML comment that’s invisible in the rendered README, so badges and prose you add survive regeneration. The customer-owned extension remains the simplest home for hand-authored code — no merge to think about at all.

Which should I use?

You want to… Use
Add new helpers, wrappers, or utilities The customer-owned extension — always preserved
Re-export or extend generated types The customer-owned extension, importing generated types normally
Change the behavior of a generated method Edit the generated file (preserved; merged in the release PR or with --merge) — or wrap it in the extension

See Drift detection for how customer-owned files are excluded from the drift gate. The CLI reference documents the commands that enforce this contract — glotto generate (whose --force overwrites managed files with local edits) and drift detection.