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.