Sync workflow
Pull, edit, push — treat content like code.
The pattern
Jaina content can be edited entirely in the GUI, entirely on disk, or mixed. The sync workflow lets you bring a package to disk, edit with your text editor or AI assistant, and push it back.
Step 1: pull
jaina pull my-game/level-1 --output ./content/
You get:
content/level-1/
├── model.json # all records in this package
└── assets/
├── goblin.png
└── boss-theme.mp3
Step 2: edit
Edit model.json directly:
{
"startsWith": "intro",
"schemaVersion": "1.0",
"package": { "slug": "level-1" },
"schemas": {
"enemy": { /* schema def */ }
},
"records": {
"intro": { /* ... */ },
"goblin_01": {
"schema": "enemy",
"data": { "name": "Goblin", "hp": 25, "attack": 3 }
}
}
}
Add new records by adding new keys under records. Drop new asset files into assets/. Use your editor of choice; AI assistants work especially well here since the format is plain JSON.
Step 3: push
jaina push ./content/level-1
The CLI computes a diff and uploads new + changed records and asset files. Removed records (keys deleted from model.json) are deleted on the server.
Preview without writing:
jaina push ./content/level-1 --dry-run
Conflicts
If someone else edits the same record in the GUI between your pull and push, the CLI detects the conflict (server updated_at newer than local pulled_at) and refuses to overwrite. Re-pull and merge.
Git-friendly
Commit model.json and assets/ directly to git. The format is stable and diffs are readable. CI can jaina push on merge.
# in CI
export JAINA_API_KEY=$JAINA_PROD_TOKEN
jaina push ./content/level-1
When to use sync vs the GUI
- Editorial work (writing copy, choosing options) → GUI.
- Bulk operations (importing 1000 records, renaming a field across many records) → sync to disk, run a script, push back.
- Versioned content (level data shipped with a release) → sync to git, ship from CI.
