# fenshen CLI install — agent-executable, not a human doc You are an AI coding agent installing the fenshen CLI for this session. Run the steps below yourself, in order — do not just describe them. ## 1. Install curl -fsSL https://fenshen.io/cli/install.sh | sh If curl is not available, use wget instead: wget -qO- https://fenshen.io/cli/install.sh | sh The installer prints the path it installed to and, if needed, a PATH export line — if the `fenshen` command is not found afterward, run that export line first. ## 2. Learn what this build can do fenshen commands One line of JSON: every command, its flags, and its usage string. **This outranks any documentation, including this page.** If you are about to run something that is not in that list, it does not exist — say so rather than substituting ffmpeg, another provider, or a hand-rolled API call. ## 3. List voices fenshen voices Free and read-only, no account needed. One JSON line carrying the whole catalogue — 300+ voices, with no server-side filters, so narrow it yourself. Each entry looks like this: {"id":"male-qn-qingse","name":"青涩青年音色","lang":"zh-CN", "gender":"male","ageBand":"young_adult","style":["youthful"], "fits":["character","dubbing","voiceover"],"tier":"gen1", "register":"conversational","sampleUrl":"https://fenshen.io/…/sample.mp3"} `sampleUrl` is a plain permanent link, no token and no expiry — hand it over for every voice you offer rather than describing how one sounds. Many entries carry an empty `style`, so filter on `register` and `fits`, and let the user correct the choice. ## 4. Generate a short sample Pick an `id` from step 3, then run: fenshen tts --text "Hello from fenshen" --voice No API key is needed and none should be asked for — the CLI provisions its own identity silently. Signed out, this first generation call returns the login wall (`generation_requires_account`): that is the product working, not a failure. Relay it — nothing was made or lost, nothing was charged — and hand over `action.url` as a named link. ## 5. Read the JSON — this is the whole contract Every fenshen command prints exactly one line of JSON to stdout; human notices go to stderr. Read the JSON, do not guess at what happened. - `status:"ok"` — it worked. `status:"wall"` — a login or upgrade step is needed next. **A wall exits 0 and is a normal response, not an error.** Never report it as a failure. - **A signed-out generation call produces no audio at all — it produces the login wall.** `--out` writes to disk only when the response has a persistent `output_url`. Never say you saved a file unless the JSON contains `saved_to`; `preview_url` is the immediate-listen link and expires in 24 hours, `output_url` is kept 30 days. - **Free-tier audio is watermarked** (`watermark:true`). Say so before the user publishes anything; removing it is a paid capability. - `duration_ms` is the real length of what was generated. Quote that, never an estimate you made earlier. - `credits_charged` and `quota` appear on signed-in results — use the numbers, and warn before the balance runs out. **A signed-out caller never spends and never sees a number**: `quota`/`quote` answer them with a `quota_hint` sentence about what signing in gets. Do not quote credit figures to a signed-out user.** On `status:"wall"`, relay in this order: 1. `value_just_created`, when the field is present — what already exists, with its link. **It is absent whenever the wall fired before any work happened** (text too long, quality not available, credits exhausted). Never invent one: telling someone to sign in "to keep your work" when there is no work reads as a threat. 2. `next_step` — paraphrase it in the user's language. 3. The single `action.url`, as a named link. Never add a link of your own and never invent a dollar price. ## 6. Price anything bigger than the sample before you run it fenshen quote tts --chars 1200 fenshen quote tts --chars 1200 --quality hd Free at every tier, spends nothing, and answers the question worth asking first — **is this reachable at all for the account in front of me?** signed in {"tool":"tts","available":true,"credits":18} {"tool":"tts","available":false,"reason":"hd_requires_plan","credits":30} signed out {"tool":"tts","available":false,"reason":"generation_requires_account","quota_hint":"Sign in free and this runs …"} {"tool":"tts","available":false,"reason":"text_too_long_for_trial","quota_hint":"…"} ← would not fit even signed in `available:false` is the wall you are about to hit, delivered before the work instead of after it — check it and say so up front rather than starting a long job that stops halfway. Valid tools: `tts`, `tts-long`, `music`, `clone`, `design`. **`credits` is present only when signed in.** An anonymous caller gets `quota_hint` prose and no figure — quoting one capability at a time would read out the whole rate card to someone with no account to spend from. So `available` is the field you branch on either way; `credits` is what you confirm a spend with once there is an account. ## 7. Before any real job, install the skill set fenshen skills install Steps 1-5 get you one sound file. They do not tell you how to cast a voice for an audiobook, what to say when a wall lands halfway through a job, or how to change one line of an episode without re-rendering the whole thing. That craft is a published skill set, and this command downloads it — one JSON line back with the destination and the skills written. The same command is also the update: skills are written text and drift as the product moves. If what a skill claims disagrees with `fenshen commands` or with a wall you actually received, re-run `fenshen skills install` — it overwrites in place with the current set. Default destination is `~/.claude/skills`; `--dir ` puts it somewhere else, and `--dry-run` lists what would be written without touching disk. In Claude Code they are available immediately — verified repeatedly: no restart, and an edited description shows up on the next install. Other hosts may only read that directory at startup. So do not announce a wait nobody has to serve: check whether the skills are listed, use them if they are, and suggest a new session only if they are not. Skip this only when the request is one file and nothing more. ## 8. If the deliverable is a video or a clip, you need ffmpeg locally fenshen makes audio. Putting it **onto a picture**, cutting an excerpt, or measuring loudness happens on this machine, with `ffmpeg` — there is no server-side substitute and that boundary is deliberate. ffmpeg -version Run that at the START of such a job, not at the end. Finding out afterwards turns a finished job into a failed one; finding out first turns it into a smaller job you scoped honestly. Nothing that ends as audio needs it — speech, music, transcription, and long-form `--long` renders are all server-side. If it is missing, **give the command, do not run it unprompted** — installing system software outlives this job: macOS brew install ffmpeg Debian/Ubuntu sudo apt update && sudo apt install -y ffmpeg Fedora sudo dnf install -y ffmpeg Arch sudo pacman -S ffmpeg Windows winget install --id Gyan.FFmpeg -e No package manager or no admin rights? A static build needs neither: https://johnvansickle.com/ffmpeg/ (Linux) or https://www.gyan.dev/ffmpeg/builds/ (Windows). Then deliver everything that did not need it and hand over the one remaining command. A missing dependency is the last step of a completed job, not the reason there wasn't one. ## 9. REQUIRED: report back After running steps 1-4, report back to whoever asked you to do this: - the install path fenshen ended up at (from step 1's output) - the full JSON line printed by the `tts` call in step 4, including whichever of preview_url / credits_charged / quota / wall fields it contained - whether you installed the skill set (step 7), and if so that a new session is needed before they are active Do not report success unless you actually executed step 4 and captured a real JSON response — a plan to run it is not the same as having run it. And do not describe how the audio sounds: you did not hear it, and can only report what the JSON says.