A CLI tool for translating book chapters with LLMs.
cipher is built for long-form translation workflows where consistency matters across many chapters. It combines profile-based provider configuration, glossary injection, validation, repair retries, and checkpointed run state so you can translate iteratively instead of treating every run as a one-shot batch job.
It is especially suited for serialized web novels and other chapter-based books, but the workflow also fits any markdown-based long-form source text.
A cipher book project is a directory containing:
- raw source chapters
- translated output
- a canonical glossary
- a style guide
- internal state used for resumability and rerun planning
For each chapter, cipher:
- loads the raw markdown
- selects glossary terms using
smartorfullinjection - sends the chapter, selected glossary, and style guide to the configured model for translation
- validates the returned translation
- attempts one repair pass if validation fails
- sends accepted output through a separate glossary extraction request
- writes accepted output atomically
- merges any newly discovered glossary terms
- saves run and chapter state under
.cipher/
This makes later runs safer and more explainable, especially when the glossary grows over time.
cargo install --git https://www.github.com/siddhj2206/cipher.gitcipher uses profiles to choose a provider and model.
cipher profile newThis interactive flow lets you:
- create or reuse a provider
- enter or reuse an API key
- choose a model
- optionally set the profile as default
Built-in providers currently include gemini and openai, and you can also add custom OpenAI-compatible providers.
You can inspect profiles with:
cipher profile list
cipher profile show myprofile
cipher profile test myprofileFrom scratch:
cipher init my-bookYou can also initialize a book with a profile or imported glossary:
cipher init my-book --profile myprofile
cipher init my-book --from other-book
cipher init my-book --import-glossary terms.jsonPlace source markdown files in raw/:
my-book/
raw/
001.md
002.md
003.md
cipher translate my-bookTranslated chapters are written to tl/.
cipher status my-bookThis shows the latest recorded run metadata and chapter summary.
my-book/
cipher.toml # Book configuration
glossary.json # Canonical glossary
style.md # Style guide injected into prompts
raw/ # Source chapters
001.md
002.md
...
tl/ # Translated output
001.md
002.md
...
.cipher/ # Internal run state, chapter state, glossary state, backups
Translate a book. If book_dir is omitted, the current directory is used.
cipher translate
cipher translate my-book
cipher translate my-book -p fast
cipher translate my-book -p best --repair-profile fast --glossary-profile cheap
cipher translate my-book -o
cipher translate my-book -d
cipher translate my-book --fail-fast
cipher translate my-book --rerun
cipher translate my-book --rerun=glossary
cipher translate my-book --rerun=source
cipher translate my-book -q
cipher translate my-book -vCurrent translate flags:
-p, --profile <name>: override the book/global profile for this run--repair-profile <name>: use a different profile for repair requests--glossary-profile <name>: use a different profile for glossary extraction requests-o, --overwrite: retranslate even when output already exists-d, --dry-run: preview translate/rerun/skip decisions without calling providers or writing state--fail-fast: stop on the first failed chapter--rerun[=MODE]: retranslate chapters affected by tracked changes. Modes:all(glossary + source, default),glossary, orsource-q, --quiet: suppress non-essential output (progress bar and detail lines)-v, --verbose: show detailed per-chapter progress and glossary info
Default behavior:
- chapters are discovered from
raw/ - chapter order is stable and numeric-first
- existing outputs are skipped unless overwrite or rerun logic applies
- output is validated before being accepted
- failed API calls retry with exponential backoff
- validation failures get one repair attempt
- accepted outputs are written atomically
- overwriting creates timestamped backups in
.cipher/backups/ - a progress bar shows translation progress (hidden with
--quiet)
Show the latest recorded run state for a book.
cipher status my-book
cipher status --jsonStatus currently includes:
- profile, provider, and model used for the last run
- start/update/finish timestamps
- chapter counts for translated, skipped, failed, and pending
- tracking counts for smart-tracked chapters, smart fallback-to-full chapters, legacy primary full-tracked chapters, approximate legacy fallback, exported-term tracking, and source hashes
- a list of failed chapters with short error previews
Create a new book scaffold.
cipher init my-book
cipher init my-book -p myprofile
cipher init my-book --from other-book
cipher init my-book --import-glossary terms.jsonManage the canonical glossary.
cipher glossary list my-book
cipher glossary list my-book --json
cipher glossary import my-book --file new-terms.json
cipher glossary export my-book --output backup.jsonManage profiles.
cipher profile new
cipher profile new --name my-profile --provider gemini --model gemini-2.5-flash --api-key-file key.txt
cipher profile new --name my-profile --no-input
cipher profile list
cipher profile list --json
cipher profile show myprofile
cipher profile show myprofile --json
cipher profile set-default myprofile
cipher profile test myprofileNon-interactive profile creation flags (all optional; omit for interactive prompts):
--name <name>: profile name (skips interactive prompt)--provider <name>: provider name (skips interactive selection)--model <name>: model name (skips interactive prompt)--key-label <label>: key label to assign (skips interactive key selection)--api-key-file <path>: read API key from file (skips key input)--set-default: set as default profile--no-input: fail if required flags are missing (for scripting)
Run diagnostics.
cipher doctor
cipher doctor my-bookWithout a book directory, doctor checks global configuration.
With a book directory, it checks book layout and effective profile resolution.
Global configuration is stored using XDG config directories. On Linux, the current path resolves to:
~/.config/cipher/config.toml
It contains:
- providers
- profiles
- default profile
Provider API keys are nested under each provider.
The current implementation stores API keys as plain text in this config. Improving secret storage is planned.
Each book contains a portable cipher.toml:
raw_dir = "raw"
out_dir = "tl"
glossary_path = "glossary.json"
style_path = "style.md"
glossary_injection = "smart"
# Optional profile overrides:
# profile = "best"
# repair_profile = "fast"
# glossary_profile = "cheap"
[output.render]
template = """
# Chapter {chapter_number}: {chapter_title}
{content}
"""Translation profile resolution order:
--profile- book
cipher.toml - global default profile
Repair and glossary extraction profiles default to the translation profile. They can be overridden with --repair-profile / --glossary-profile or persistent repair_profile / glossary_profile values in cipher.toml.
The glossary is a JSON array of terms:
[
{
"term": "Starship",
"og_term": "星空舰",
"definition": "The main character's vessel"
},
{
"term": "River Map",
"og_term": "山河图",
"definition": "An ancient artifact containing a sealed dimension",
"notes": "Sometimes referred to as 'The Map' in casual dialogue"
}
]Fields:
term: translated term to enforceog_term: original-language term used for matchingdefinition: explanation/contextnotes: optional extra guidance
Glossary behavior:
- canonical source of truth is
glossary.json - merges are deterministic
- duplicate terms are skipped during merge/import
- new terms returned by successful chapters are appended after dedupe
smart is the canonical/default mode. Legacy full config values are treated as smart.
Smart-mode behavior:
- matches glossary terms against the chapter text using deterministic selection logic
- always includes terms with empty
og_term - falls back to full glossary when too few matches are found
- legacy primary full-tracking state is migrated opportunistically when a successful smart-era run proves it is equivalent to smart fallback tracking
If present, style.md is injected into every translation request. Put book-specific formatting guidance there, including how to populate structured output fields for a custom render template.
Use it for:
- tone
- narration style
- dialogue conventions
- recurring translation preferences
- rules that are broader than glossary terms
Before output is accepted, cipher validates it.
Validation checks include:
- non-empty output
- heading presence/shape
- balanced code fences
- JSON/schema leakage detection
- rejection of raw structured response artifacts leaking into prose
If validation fails:
- the failure is recorded
- one repair request is attempted using the original text, failed translation, and validation errors
- the repaired output is validated again
- if it still fails, the chapter is marked failed
Glossary extraction now runs only after a translation has passed validation.
- translation requests return translated markdown only
- repair requests return corrected markdown only
- glossary extraction runs as a separate follow-up call against accepted markdown and existing glossary terms
- glossary extraction failure does not invalidate an otherwise accepted chapter; it only skips adding new terms for that chapter
cipher stores internal state under .cipher/ so runs are resumable and future rerun decisions can be more informed.
Tracked state includes:
- run metadata
- per-chapter result state
- glossary-state snapshots
- chapter glossary usage
- exported glossary term fingerprints
--rerun=glossary uses tracked state to detect when a chapter should be rerun because glossary-relevant inputs changed.
Rerun detection compares saved chapter glossary state against the current expected glossary usage, including changed term fingerprints, smart-selection changes when newly relevant or removed terms alter the effective injected set, and fallback-to-full behavior changes. Forward-only incremental replanning for remaining chapters runs when new glossary terms are discovered mid-run.
These are different tools:
--overwritemeans redo outputs regardless of tracked equivalence--rerun(or--rerun=all) means rerun chapters whose tracked source or glossary inputs changed--rerun=glossarymeans rerun chapters whose tracked glossary inputs became stale--rerun=sourcemeans rerun chapters whose tracked raw source became stale
Current file-safety behavior:
- accepted outputs are written atomically
- overwriting creates backups in
.cipher/backups/ - glossary and state are saved incrementally during runs
This keeps runs resumable and reduces the chance of corrupted outputs after interruptions.
- API keys are stored as plain text in global config; a proper secret store is planned
- dry-run preview reports planned actions from the existing rerun rules
- status output does not expose all tracked-vs-approximate rerun details
Useful commands while working on the project:
cargo build
cargo check
cargo fmt
cargo test
cargo run -- translate ./test-book
cargo run -- status ./test-book
cargo run -- doctor ./test-book