Metadata-Version: 2.4
Name: motifkit
Version: 0.1.0
Summary: Generate a drop-in animated mascot from the command line
Author: Motif
License: Proprietary
Project-URL: Homepage, https://trymotif.app
Keywords: sprite-sheet,animation,mascot,gamedev
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# motif

Generate a drop-in animated mascot for any app or game, from the command line.

One description plus an action becomes a sprite sheet with the background removed,
frames sliced and normalized, and export bundles for iOS, Android, Web, Unity and
Godot. The work happens on trymotif.app and spends credits from your account, so this
tool needs neither an image-model key nor ffmpeg on your machine.

Two tiers: the sprite sheet above, and **Motion AI** (`--mode motion`), which
animates a picture of your character with a video model instead of drawing one.

## Install

```bash
pipx install motifkit
```

No dependencies beyond Python 3.10+. Nothing is compiled, nothing is downloaded at
runtime.

## Set up

The hosted API has to be switched on for your deployment - if the API-keys panel is
not in Settings, it is not on yet.

Create a key in Settings, then:

```bash
motif configure
```

It is stored in `~/.config/motif/credentials.json`, readable only by you. In CI, skip
that and set the key from your secret store instead - the environment always wins:

```bash
export MOTIF_API_KEY="motif_sk_..."
```

## Use

```bash
# The whole thing: generate, wait, and write the bundle.
motif gen "a happy robot mascot" --action "jumping for joy"

# With a reference image, a grid, and somewhere to put it.
motif gen "a fox knight" \
  --action "waving hello" \
  --ref ./concept.png \
  --rows 2 --cols 4 --fps 12 \
  --out ./assets/fox

# What would this cost? Creates nothing.
motif gen "..." --action "..." --dry-run
motif account
```

## Motion AI (`--mode motion`)

The other tier. Instead of drawing a sprite sheet, Motion AI films **your
character's own artwork** with a video model, so it needs a picture of the
character and costs more (ask the server: `motif account`, which also says
whether this deployment offers the tier through the API at all).

```bash
motif motions                       # the motions this server ships

motif gen --mode motion --action wave --ref ./capy.png --out ./assets/capy
```

`--action` is either one of those preset keys or your own description of the
movement (`--action "sips from a mug and smiles"`).

**The cutout comes first.** A clip is generated from one exact cut-out image, and
whatever that image looks like is what the mascot IS for the whole clip - a matte
that ate an ear produces a clip of a one-eared mascot, and nothing downstream can
put it back. So the first run cuts the character out, writes the PNG next to your
work, and stops:

```
Cutout written to ./motif-cutout-8f21ba03.png (512x512).
Nobody has approved this cutout yet. Open it, and if the character survived the
cut, run the same command again with --approve-cutout.
```

Look at it, then:

```bash
motif gen --mode motion --action wave --ref ./capy.png --approve-cutout
```

Nothing is charged until that point. The approval belongs to the mascot rather
than to each animation, so later clips of the same character reuse it:

```bash
motif gen --mode motion --action nod --project <project_id>
```

The project id is printed with the job; `--cutout-out PATH` chooses where the
PNG lands. Replacing the reference image retires the approval, on purpose.

**The default cut changed.** `motif gen --mode motion` now exports the whole
take rather than one cycle of it, matching the Studio. A script that relied on
the old default gets a longer animation - same credits, more frames, a bigger
bundle, and it no longer loops seamlessly. Pass `--cut loop` to keep the old
behaviour. The recovery command printed on failure always spells `--cut` out in
full, so a captured command reproduces its own request whatever the default is.

One more thing to know if you abbreviate flags: `--cut` used to be an
unambiguous prefix of `--cutout-out`, so `--cut ./x.png` meant "write the cutout
there". It is now a flag in its own right and that spelling is refused with the
valid choices named, rather than quietly meaning something else.

| Option | Default | Meaning |
| --- | --- | --- |
| `--mode motion` | `sprite` | Use Motion AI instead of a sprite sheet |
| `--duration N` | `5` | Seconds of footage to buy (priced per length) |
| `--cut whole\|loop` | `whole` | `whole`: the entire take, played once - it steps where it wraps. `loop`: one cycle, about a second, repeating seamlessly |
| `--whole-clip` | off | Deprecated alias for `--cut whole` |
| `--approve-cutout` | off | Approve the written cutout without being asked |
| `--cutout-out PATH` | `./motif-cutout-<id>.png` | Where to write the cutout for review |
| `--project ID` | - | Reuse a mascot's approved cutout |

`motif gen` covers upload, create, poll and download, so you never have to handle a
job id - until something goes wrong, and then it prints the command to recover with:

```bash
motif status <job_id> --wait     # keep waiting, then download
motif status <job_id>            # just look
motif export <job_id> --out ./x  # download again
motif cancel <job_id>            # ask the server to stop
motif cut <job_id> --cut loop    # re-cut the clip - costs nothing
motif jobs                       # list recent jobs, newest first
```

`motif jobs` is the answer to "I lost the id". A generation is paid for the moment
it is admitted, so a job id that went missing is money already spent; this lists
what is still there to poll or download.

`motif cancel` stops a job that has not started spending. Once the work has been
sent to the model it is paid for, so that job finishes and keeps its credits -
`motif status` says which happened.

`motif cut` changes a finished clip between the whole take and the loop. The
footage is already bought, so switching is free and unlimited - you never have to
regenerate to see the other cut. Add `--out ./x` to download the re-cut bundle in
the same step.

Pressing Ctrl-C stops the CLI, not the job. The generation is already paid for and
keeps running, so the CLI tells you the id to resume with rather than silently
throwing the work away.

Waiting rides out a bad connection. A clip can take several minutes and is checked
every few seconds, so the CLI retries a dropped read with a backoff rather than
abandoning a job you have already paid for, and it obeys the server's `Retry-After`
when it is asked to slow down. It gives up only after five failures in a row.

## In a script

```bash
motif gen "a happy robot" --action idle --json > result.json
case $? in
  0) jq -r '.output_dir' result.json ;;
  4) echo "out of credits"; exit 1 ;;
  6) sleep 60 ;;   # rate limited
esac
```

In motion mode a script (or an agent) has one extra step, and it is deliberate:
run once, look at the cutout the CLI wrote, then re-run with `--approve-cutout`.

```bash
motif gen --mode motion --action wave --ref ./capy.png --cutout-out ./cut.png
if [ $? -eq 10 ]; then
  # ./cut.png is on disk. Check the character survived the cut, then:
  motif gen --mode motion --action wave --ref ./capy.png --approve-cutout --json
fi
```

`--json` prints the machine-readable result on stdout; progress and errors go to
stderr, so redirecting one does not lose the other. The output never contains your
API key.

Exit codes are part of the contract, grouped by what you have to do about them:

| Code | Meaning |
|---:|---|
| 0 | success |
| 1 | unexpected failure |
| 2 | bad arguments or local config |
| 3 | missing, invalid or revoked API key |
| 4 | not enough credits (or the free daily limit) |
| 5 | idempotency conflict, or too late to cancel |
| 6 | rate limited |
| 7 | the generation itself failed or was cancelled |
| 8 | connection lost or interrupted - the job may still be running |
| 9 | this build is older than the server accepts |
| 10 | the cutout needs looking at before a clip can be made (motion mode) |

### About retrying

Each invocation carries one idempotency key, so a retry **inside** that invocation -
after a dropped connection, say - returns the job that already exists instead of
creating a second one.

Running the command again is a **new** request and is charged again. That is
deliberate: two `motif gen` commands usually mean you want two animations. If you are
retrying rather than asking for another one, resume the job you already paid for:

```bash
motif status <job_id> --wait
```

Exit code 8 always means "the job may still be running", and it always prints that
command.

