# @remixmate/cli

English | [简体中文](./README.zh-CN.md)

> Formerly `ab-skill-cli` (bin `ab-skill`), now renamed to **`@remixmate/cli`** (bin **`remixmate`**). The old package is deprecated on npm — please migrate to the new one.

AI media generation skills for Claude Code / Codex.

13 skills covering the full short-video production pipeline: image / video / voice / digital-human asset generation, web page capture and text extraction, script planning, template binding, Remotion rendering, Jianying (CapCut) draft export, and video deconstruction.

## Install

```bash
npm install -g @remixmate/cli && remixmate install
```

`npm install` gives you the `remixmate` binary. `remixmate install` is what makes an
agent *discover* the skills on its own: it copies the bundled `skills/` into every
agent home it finds. All of these hosts read the same `SKILL.md` format, so one
`skills/` directory serves all of them:

| Host | User-level | Project-level | Override |
| --- | --- | --- | --- |
| Claude Code | `~/.claude/skills` | `./.claude/skills` | `CLAUDE_CONFIG_DIR` |
| Codex | `~/.codex/skills` | — | `CODEX_HOME` |
| WorkBuddy | `~/.workbuddy/skills` | `./.workbuddy/skills` | — |
| CodeBuddy | `~/.codebuddy/skills` | `./.codebuddy/skills` | — |
| Trae / TraeWork | — | `./.trae/skills` | — |

Trae CN and TraeWork CN share `~/.trae-cn` and only define a workspace location (`.trae/skills/<name>/`, per its own built-in
`skill-creator`), so it is installed per project — a plain `remixmate install` reports
it and tells you to re-run with `--project` inside the repo. Restart the agent
afterwards.

```bash
remixmate install --host workbuddy  # only one host
remixmate install --project         # ./<config-dir>/skills (not Codex — it has none)
remixmate install --dir <path>      # explicit target, for a host not listed above
remixmate uninstall                 # remove exactly what was installed
```

The install is *managed*, not a `cp -R`: each skills directory gets a
`.remixmate-install.json` receipt recording the version and the skill list. That is
what lets a re-run upgrade in place, lets `uninstall` remove only our own directories,
and lets `remixmate doctor` warn when a global `npm update` has left the installed
copies behind. A same-named skill that this CLI did not install is never overwritten
without `--force`.

Without installing, the CLI still works — you just have to name it (`remixmate --list`,
or tell the agent to run `remixmate <skill> --help`). The agent will not reach for it
unprompted.

## Authentication

The recommended way to authenticate (for humans installing the CLI) is a browser
device-login — no token pasting required:

```bash
remixmate login     # opens your browser to approve, then stores credentials securely
remixmate whoami    # show the current identity (never prints the token)
remixmate logout    # remove stored credentials from this machine
```

`login` uses the OAuth 2.0 Device Authorization Grant (RFC 8628): the CLI shows a
device code, you approve it in a browser that is already signed in to the web app, and
the CLI receives and stores your credential in the OS keychain (or a `0600` file at
`~/.config/remixmate/credentials.json`).

No TTY is required — the device flow reads no keyboard input, so an agent host
(Claude Code / Codex) can run it and relay the authorization link into the chat.
The CLI prints a link with the device code pre-filled, so approving is one click.
It is refused only where a browser could never be reached: CI, a headless Linux
session, or `REMIXMATE_NO_BROWSER_AUTH=1`; use `PRIV_TOKEN` there instead.

For CI and agent-embedded hosts (ab-agent / Claude Code), keep using the `PRIV_TOKEN`
environment variable. Credential resolution precedence is:

```
--token flag  >  PRIV_TOKEN env var  >  stored credential (from `remixmate login`)
```

When `PRIV_TOKEN` is set, `login` is skipped and the device flow is never auto-triggered.

The credential is resolved once, on the Node side (`src/auth/ensure.ts`), and injected
into spawned Python skills through their environment — skill scripts never read the
credential store or the keychain themselves. To give any other command the same
credential, use `exec`:

```bash
remixmate exec -- python3 scripts/test-template-pipeline.py
```

Each skill declares its requirement as `auth` in `skill.json`: `required` (authorize
when no token is present), `optional` (use a token if there is one, otherwise carry on
in a degraded mode — `web-record` keeps the file locally), or `none` (purely local, e.g.
`web-screenshot`).

Exit codes on failure: `4` = authorization needed, `5` = backend unreachable,
`2` = usage error.

### Non-blocking authorization (agent hosts)

A blocking `login` waits for the device code's full lifetime, which outlives an
agent's time-bounded tool call. Split it instead:

```bash
remixmate login --start --json    # returns immediately with the approval link
remixmate login --wait --timeout 60 --json   # bounded poll; call again while pending
```

`--start` persists the device code to a `0600` `~/.config/remixmate/pending.json`;
`--wait` redeems it and clears the file. While the user has not yet approved,
`--wait` reports `{"status":"pending"}` with exit code 4 and leaves the pending
state in place, so it is safe to call repeatedly.

### Diagnosing

```bash
remixmate doctor            # node / python3 / Playwright / credential / backend
remixmate doctor --offline  # skip the reachability probe
```

`doctor` exits `4` when the only problem is a missing credential.

### Listing skills

`remixmate --list` prints a human-readable table (name + whether it needs a login
+ summary) and appends the current auth status on stderr, so piping stdout is
unaffected. For tooling, use `remixmate --list --json`.

### Automatic browser authorization (local hosts)

When a skill that needs a credential is invoked from a **local third-party host**
(Claude Code / Codex / a terminal) and none of `--token` / `PRIV_TOKEN` / the stored
credential yields a token, the CLI automatically runs the device flow once: it opens
your browser, prints the verification URL + code, waits a short window, and on approval
stores the credential — so every later call resolves silently.

This only fires on a **browser-capable local desktop**. It never fires when `PRIV_TOKEN`
is injected (cloud hosts) or in **headless** environments (CI / no GUI / no `DISPLAY`),
which keep the existing fail-fast behavior. Controls:

```
REMIXMATE_NO_BROWSER_AUTH=1     # force-disable auto auth (always fail-fast)
REMIXMATE_AUTH_WAIT_SECONDS=25  # short-wait window (default 25s); on timeout it
                                # tells you to authorize and re-run, never blocks forever
```

> Security: if you previously pasted your `PRIV_TOKEN` into a chat box or shell history,
> rotate it after switching to `remixmate login`.

## Environment

Most skills authenticate against ab-api with a `PRIV_TOKEN`. Without a valid token (and network access to ab-api) the template / media-generation skills are unavailable — the code is open-source, but the generation capabilities are hosted on the ab-api service. Copy `.env.example` to `.env` and source it before running:

```bash
cp .env.example .env
# edit values
source .env
```

Python skills (9 of 13) require `python3 >= 3.10`. `web-screenshot` / `web-record` / `web-read` require Playwright (auto-installs chromium on first run). `ffmpeg` is only needed for `video-parser`'s optional local tool (`deconstruct_video.py`); the default `video-parser` entry runs server-side via ab-render and needs no local ffmpeg.

## Skills

This project contains 13 AI media generation skills covering the full pipeline from asset generation, script orchestration, template binding, and video rendering to Jianying export.

### Skill layers

```
┌─────────────────────────────────────────────────────────────┐
│ Orchestration Skills                                         │
│  gen-script           Topic → Video DSL (script generation) │
│  template-registry        DSL → TemplateBinding (template list) │
│  prepare-video-assets DSL + Binding → asset resolve (Phase1)│
│  render-video         RenderPlan → Remotion render (Phase 3)│
│  export-jianying      Assets → Jianying draft ZIP           │
├─────────────────────────────────────────────────────────────┤
│ Atomic Skills                                                │
│  gen-image         Text/Image-to-image (Seedream 5.0)       │
│  gen-video         Text-to-video (Seedance 2.0)             │
│  gen-voice         Text-to-speech (Minimax TTS)             │
│  gen-digital-human Talking-head (Jimeng / Feiying)          │
├─────────────────────────────────────────────────────────────┤
│ Tool Skills                                                  │
│  video-parser      Video deconstruction (audio / ASR / kf)  │
│  web-screenshot    Web page screenshot (png / jpg)          │
│  web-record        Web page recording / storyboard video    │
│  web-read          Web page text extraction (markdown/json) │
└─────────────────────────────────────────────────────────────┘
```

### Directory structure

```
├── docs/
│   └── video-production-architecture.md   # architecture docs
├── src/                        # TypeScript CLI + http/builtin handlers
├── skills/
│   ├── gen-image/              # atomic: AI image generation (http handler)
│   ├── gen-video/              # atomic: AI video generation (http handler)
│   ├── gen-voice/              # atomic: text-to-speech (http handler)
│   ├── gen-digital-human/      # atomic: talking-head video (http handler)
│   ├── gen-script/             # orchestration: Topic → Video DSL
│   ├── template-registry/          # orchestration: template list (python list_templates.py)
│   ├── prepare-video-assets/   # orchestration: Phase 1 asset prep (thin wrapper)
│   ├── render-video/           # orchestration: Phase 3 Remotion render (canonical render_video.py)
│   ├── export-jianying/        # orchestration: export Jianying draft ZIP
│   ├── video-parser/           # tool: video deconstruction & analysis
│   ├── web-screenshot/         # tool: headless-browser screenshot (also hosts record.py)
│   ├── web-record/             # tool: headless-browser recording (entry -> web-screenshot/scripts/record.py)
│   └── web-read/               # tool: headless-browser text extraction (entry -> web-screenshot/scripts/read_page.py)
└── README.md
```

### Skill reference

| Skill | Type | Description | Runtime / entry |
|-------|------|-------------|-----------------|
| gen-image | atomic | Text/Image-to-image (Seedream 5.0 Lite / Pro) | http handler |
| gen-video | atomic | Text-to-video (Seedance 2.0, three tiers) | http handler |
| gen-voice | atomic | Text-to-speech (Minimax TTS) | http handler |
| gen-digital-human | atomic | Talking-head (Jimeng / Feiying) | http handler |
| gen-script | orchestration | Topic → Video DSL JSON | python `scripts/gen_script.py` |
| template-registry | orchestration | Template list (binding logic embedded in prepare-video-assets) | python `scripts/list_templates.py` |
| prepare-video-assets | orchestration | DSL + Binding → asset resolve → persist RenderPlan | python `scripts/prepare_video_assets.py` (wraps `render_video.py --resolve-only`) |
| render-video | orchestration | job_id → Remotion render → upload | python `scripts/render_video.py` |
| export-jianying | orchestration | Asset URLs → Jianying draft ZIP (auto-converts from RenderPlan) | python `scripts/gen_jianying_draft.py` |
| video-parser | tool | Video → audio + ASR + keyframes + scene segmentation | python `scripts/parse_via_render.py` |
| web-screenshot | tool | Web page screenshot (png / jpg) | python `scripts/screenshot.py` |
| web-record | tool | Web page recording / scroll-through / storyboard video (webm -> mp4 -> VOD) | python `../web-screenshot/scripts/record.py` |
| web-read | tool | Web page → main text (markdown / plain text / structured JSON) | python `../web-screenshot/scripts/read_page.py` |

### Core workflows

See the [video production architecture doc](docs/video-production-architecture.md) and the [orchestration guide](docs/orchestration-guide.md).

**Workflow A: Topic → Remotion video**
```
gen-script → ✅ user confirms script → prepare-video-assets(--template-id)
→ ✅ user confirms assets → render-video(--job-id) → MP4
```

**Workflow B: Topic → Jianying draft**
```
gen-script → ✅ user confirms script → prepare-video-assets(--template-id)
→ ✅ user confirms assets → export-jianying(--from-job-id) → Jianying ZIP
```

### Testing

```bash
# List all skills (name, tool, entry type)
remixmate --list

# Offline registry smoke + spec guards
npm run smoke

# CLI unit tests (argv parser + skill schema)
npm run test:cli

# ── Template regression (after changing binding / timeline / template code) ──
# L1 (contracts) + L2 (render-plan golden snapshots), full coverage, seconds,
# no render and no generation API calls. Run this before/after touching shared
# template plumbing to catch "did I break some OTHER template's binding?".
npm run regress

# Accept intended render-plan changes (rewrites L2 baselines):
npm run regress:update

# Run only the render-plan snapshot layer:
npm run test:render-plan

# Narration speed wiring (gen_script → render plan → gen-voice --speed).
# Fully offline: no registry, no TTS call.
npm run test:narration-speed

# L3 — selective, real Remotion render of chosen template(s). NOT full-library
# by design (rendering is slow + needs asset generation). Catches crashes /
# black frames the static layers can't. Needs PRIV_TOKEN for narration TTS;
# pass --stub-image-url to keep image-heavy renders cheap.
npm run check:render -- --template spotlight-card --frame

# Template defs come from the registry (ab-api HTTP + /tmp cache), exactly like
# render_video in production. A warm cache or reachable ab-api is required.
# In a multi-repo checkout the scripts auto-detect template-library; override
# with AB_TEMPLATES_SRC=/path/to/template-library/packages/templates/src.

# Python skills forward --help to the underlying script:
remixmate gen-script --help
remixmate prepare-video-assets --help
remixmate render-video --help
remixmate export-jianying --help
remixmate video-parser --help
remixmate web-screenshot --help
remixmate web-record --help
remixmate template-registry --help

# http skills (gen-image, gen-video, gen-voice, gen-digital-human) take no
# python --help; their flags live in each SKILL.md / skill.json.

# List available voices
remixmate gen-voice --list-voices

# List available templates
remixmate template-registry --list-templates
```

### Development

- Clone this project locally
- Develop inside each skill folder under `skills/`
- Each skill contains `SKILL.md` (skill docs), `version.json` (metadata), and `scripts/` (scripts)

### Quick start

```
@skills/gen-image/SKILL.md Generate an image of a panda, 9:16, using seedream-pro, Chinese-painting style + follow this doc strictly
@skills/gen-video/SKILL.md Generate a video of a panda running in a bamboo forest, 9:16, 6 seconds, using seedance-mini + follow this doc strictly
@skills/gen-voice/SKILL.md Generate a voiceover introducing panda habits, around 100 words + follow this doc strictly
@skills/gen-digital-human/SKILL.md Get the digital human list + follow this doc strictly
@skills/template-registry/SKILL.md Get the template list + follow this doc strictly
@skills/gen-script/SKILL.md Based on the image-slide template, create a video about AI learning methods + follow this doc strictly
```

## License

MIT
