This file provides guidance for AI agents (Claude Code and subagents) working in this repository.
Templates are not used directly — they're rendered at generation time. The examples/ folder is the ground truth for what the generator actually produces. Always regenerate after making template changes.
The generator does not auto-discover templates. Every output file must have an explicit entry in the getFiles(): array method of the corresponding Language class. Forgetting this means the file silently doesn't get generated.
| Parent | Children affected |
|---|---|
Node |
CLI, ReactNative |
Dart |
Flutter |
Swift |
Apple |
Kotlin |
Android |
Modifying a parent's template or getFiles() affects all children. Regenerate and verify child SDKs too.
Files with 'scope' => 'copy' are copied verbatim — no variable substitution happens. If your new file needs template variables, use 'scope' => 'default' (or service, method, etc.).
The destination string in each getFiles() entry supports Twig expressions and filters:
'destination' => 'src/Services/{{ service.name | caseCamel }}.php',- Purpose: Generate Appwrite SDKs for ~16 languages from Swagger/OpenAPI specs using Twig templates
- Language: PHP (generator engine) + Twig (templates)
- Entry point:
example.php— runs generation for all or a specific SDK - Output:
examples/<lang>/— checked-in generated SDK output for verification
src/SDK/Language/<Lang>.php ← Language class: defines files, types, keywords
templates/<lang>/ ← Twig templates for that language
examples/<lang>/ ← Generated SDK output (checked in for verification)
example.php ← Entry point: regenerates all SDKs from specs
Supported SDKs: PHP, Web, Node, CLI, Ruby, Python, Dart, Flutter, React Native, Go, Swift, Apple, DotNet, Android, Kotlin, GraphQL, Markdown, AgentSkills
- Edit template(s) in
templates/<lang>/ - Regenerate:
docker run --rm -v $(pwd):/app -w /app php:8.3-cli php example.php <lang>
- Diff
examples/<lang>/to verify the output is correct - Run linter:
composer lint-twig # or directly uvx djLint templates/ --lint
- Create the
.twigfile intemplates/<lang>/ - Register it in
src/SDK/Language/<Lang>.php→getFiles()array — this is mandatory:
public function getFiles(): array
{
return [
// ...existing entries...
[
'scope' => 'default', // default|service|method|definition|requestModel|enum|copy
'destination' => 'path/to/output.ext',
'template' => 'lang/path/to/template.twig',
],
];
}Scopes:
default— generated once per SDK (config files, README, main entry point)service— generated once per API servicemethod— generated once per service×method combinationdefinition— generated once per model/definitionrequestModel— generated once per request modelenum— generated once per enumcopy— static files copied as-is, no Twig processing
- Regenerate and verify
- Create
src/SDK/Language/NewLang.php(extendLanguageor a related language) - Implement:
getName(),getKeywords(),getIdentifierOverrides(),getFiles(),getTypeName(),getParamDefault(),getParamExample() - Create
templates/newlang/and add all Twig files - Register all template files in
getFiles() - Add generation block to
example.php - Generate:
docker run --rm -v $(pwd):/app -w /app php:8.3-cli php example.php newlang - Inspect
examples/newlang/
| What you want to change | Where to look |
|---|---|
| Template for a language | templates/<lang>/ |
| Which files get generated | src/SDK/Language/<Lang>.php → getFiles() |
| Type mappings for a language | src/SDK/Language/<Lang>.php → getTypeName() |
| Available Twig filters | src/SDK/SDK.php (around line 62) |
| How specs are parsed | src/Spec/Swagger2.php |
| Generation orchestration | src/SDK/SDK.php → generate() |
| Example generation script | example.php |
| Generated output for review | examples/<lang>/ |
Pass as first argument to generate only that SDK:
| Argument | Language class | Output dir |
|---|---|---|
php |
PHP | examples/php/ |
web |
Web | examples/web/ |
node |
Node | examples/node/ |
cli |
CLI | examples/cli/ |
ruby |
Ruby | examples/ruby/ |
python |
Python | examples/python/ |
dart |
Dart | examples/dart/ |
flutter |
Flutter | examples/flutter/ |
react-native |
ReactNative | examples/react-native/ |
go |
Go | examples/go/ |
swift |
Swift | examples/swift/ |
apple |
Apple | examples/apple/ |
dotnet |
DotNet | examples/dotnet/ |
android |
Android | examples/android/ |
kotlin |
Kotlin | examples/kotlin/ |
graphql |
GraphQL | examples/graphql/ |
markdown |
Markdown | examples/markdown/ |
| Scope | Extra variables available |
|---|---|
| All scopes | spec, language, sdk |
service |
+ service |
method |
+ service, method |
definition |
+ definition |
requestModel |
+ requestModel |
enum |
+ enum |
- Silent no-op: A new
.twigfile with nogetFiles()entry — generation runs successfully but the file is never created - Wrong scope: Using
defaultscope when you needservicescope means your template can't access{{ service.name }} - Copy scope surprises: A
copy-scoped file with Twig syntax — the syntax is output literally, not rendered - Spec fetch failure:
example.phprequires internet access to fetch the live spec from GitHub; generation fails with an exception if the fetch returns empty. Spec URL pattern:https://raw.githubusercontent.com/appwrite/specs/main/specs/{version}/swagger2-{version}-{platform}.json - Platform mismatch: Pass the right platform (
console,client,server) as second arg — different platforms expose different API services - Child language gaps: Adding a file to a parent's
getFiles()but the child language needs a different template — child classes can overridegetFiles()to replace or remove entries
# With Composer installed locally
composer update --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist
# With Docker
docker run --rm -it -v "$(pwd)":/app composer update --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-distdocker run --rm -v $(pwd):$(pwd):rw -w $(pwd) php:8.3-cli-alpine vendor/bin/phpunitBefore submitting changes that touch templates or language classes:
- Regenerated the affected SDK(s) with
example.php - Inspected
examples/<lang>/output looks correct - Any new template files are listed in
getFiles()of the language class - Any new language class is added to
example.php - Twig linter passes (
composer lint-twig) - If a parent language was modified, child SDKs were also checked