translate-book — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited translate-book (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Translate all chapters in parallel with glossary consistency, producing a translated EPUB. Uses XHTML tokenization to preserve formatting, dispatches translation agents in batches of 8, validates token integrity, merges new terms, and rebuilds the final EPUB.
<!-- ANTI-PATTERNS — READ BEFORE PROCEEDING:
-->
Print the version on every run:
cabt:translate-book v1.1.3Then check if $ARGUMENTS contains --version. If it does, stop here -- do not proceed to Step 1 or any further steps.
If a workspace path was provided as $ARGUMENTS, change to that directory first:
cd "<workspace_path>"Read metadata.json from the current directory.
cat ./metadata.jsonVerify the contents:
status is not "glossary-built" and not "translating", tell the user: "Unexpected workspace status: '{status}'. Expected 'glossary-built' (run /build-book-glossary first) or 'translating' (for resume)." and stop.Verify that glossary.md exists in the current directory:
test -f ./glossary.md && echo "Glossary found" || echo "Glossary not found"If glossary.md does not exist, tell the user: "glossary.md not found. Run /build-book-glossary first." and stop.
Extract these fields for use throughout the workflow:
titlegenresource_languagetarget_languagechapters (array with sequence, output_filename, token_count)original_epub_pathAlso extract reuse information:
glossary_reuse field (if present): contains source_workspace, source_book_title, imported_files, imported_atglossary_reuse is present AND glossary_reuse.imported_files contains "translation-style-brief.md" AND ./translation-style-brief.md exists on disk -- if all true, this is a reuse path for the style briefglossary_reuse is absent or "translation-style-brief.md" is not in imported_files or file is missing on disk, this is a fresh path (v1.0 behavior unchanged)Note: Do NOT check for translation_progress here -- that is the resume path handled by Step 2. The three paths are: resume (translation_progress present with brief_generated or brief_reused true), reuse ("translation-style-brief.md" in glossary_reuse.imported_files + file on disk), and fresh (no brief). Resume takes precedence over reuse.
Check if metadata.json has a translation_progress field:
Tell the user: "Translation already complete. Your EPUB is at: ./{title}-{target_language}.epub" and stop.
This is a resume. Determine:
chapters_translated: list of chapter sequences already translatedchapters_failed: list of chapter sequences that previously failed (will be retried)chapters_remaining: all other chaptersbrief_generated or brief_reused field)Tell the user: "Resuming translation: {N} chapters already translated, {M} remaining (including {F} previously failed chapters to retry)."
If brief_generated is true or brief_reused is true, skip Step 2.5 and Step 4 (style brief already handled). Skip chapters in chapters_translated during Step 7. Retry chapters in chapters_failed along with chapters_remaining.
Skip this step on resume if `translation_progress.brief_generated` is true or `translation_progress.brief_reused` is true.
Check which path applies (in priority order):
"translation-style-brief.md" is in glossary_reuse.imported_files AND ./translation-style-brief.md exists on disk -- run the analysis flow below.Reuse path (analysis flow):
Print:
Found translation style brief from [source_book_title]. Analyzing compatibility...Where [source_book_title] comes from glossary_reuse.source_book_title in metadata.json.
Read the existing brief:
cat ./translation-style-brief.mdRead book_research from metadata.json (populated by /build-book-glossary Step 4). Compare the new book's research against the existing brief's settings. Specifically look for:
IMPORTANT: Do NOT flag these as differences:
If differences ARE found: Present the brief summary with key settings, then flag each difference explicitly. For each diff, show what the current brief says and what the research suggests, then ask for the user's decision. Example:
Your brief says 'comedic tone with wordplay adaptation.' The new book has a more serious, dramatic tone. Want to adjust the humor handling?After the user responds to each flagged item, update ./translation-style-brief.md in place with the changes (edit specific sections, no full rewrite).
Then ask: 'Anything else you'd like to adjust?'
If NO meaningful differences found: Show:
Using translation style brief from [source_book_title]. Style: [key settings summary extracted from the brief -- sentence structure, humor handling, reading level, cultural references]. Anything to adjust?Wait for user response. If user has adjustments, update ./translation-style-brief.md in place. If user confirms, proceed.
'Start fresh' escape hatch: If at any point the user says they want to start fresh / generate a new brief / discard the existing brief:
brief_reused flagAfter confirmation (reuse path only):
mkdir -p './output/{lang}/'
mkdir -p './output/{lang}/translator-notes/'
mkdir -p './output/{lang}/new-terms/'
mkdir -p './output/{lang}/.tokenized/'
mkdir -p './output/{lang}/.translated/'cp './translation-style-brief.md' './output/{lang}/translation-style-brief.md'status to 'translating' and add translation_progress with:status: 'in_progress'brief_reused: truebrief_generated: falsechapters_translated: []chapters_failed: []total_chapters: (count from chapters array)Skip this step if output directories were already created in Step 2.5 (reuse path).
Determine the language directory name. Use the same naming convention as prepare_workspace.py: lowercase, spaces replaced with hyphens (e.g., "Brazilian Portuguese" becomes "brazilian-portuguese").
mkdir -p "./output/{lang}/"
mkdir -p "./output/{lang}/translator-notes/"
mkdir -p "./output/{lang}/new-terms/"
mkdir -p "./output/{lang}/.tokenized/"
mkdir -p "./output/{lang}/.translated/"Where {lang} is the target language directory name (lowercase, hyphenated).
Update metadata.json: set status to "translating" and add translation_progress with:
status: "in_progress"brief_generated: falsechapters_translated: []chapters_failed: []total_chapters: (count from chapters array)Skip this step on resume if `translation_progress.brief_generated` is true. Also skip this step if translation_progress.brief_reused is true (brief was confirmed from reuse in Step 2.5).
Read glossary-rules.md to understand existing translation philosophy decisions:
cat ./glossary-rules.mdPresent 3-4 translation-specific questions to the user that are NOT covered by glossary-rules.md. Use multiple-choice format (A/B/C). These questions address translation approach, not terminology (which glossary-rules.md already covers).
Example questions (use your judgment for exact wording based on genre and language pair):
Wait for the user's responses in the conversation. Do NOT use AskUserQuestion.
After collecting all answers, build the Q&A JSON and call translation_helpers.py:
python ${CLAUDE_PLUGIN_ROOT}/scripts/translation_helpers.py generate-brief \
--metadata-path ./metadata.json \
--qa-json '{"sentence_structure": "answer", "humor_handling": "answer", "reading_level": "answer", "cultural_references": "answer"}' \
--output ./output/{lang}/translation-style-brief.mdRead the generated brief and show a summary to the user:
cat ./output/{lang}/translation-style-brief.mdUpdate metadata.json: set translation_progress.brief_generated to true.
Read glossary.md and strip (?) markers using translation_helpers.py. This creates a clean copy for translation agents -- the original glossary.md is NOT modified.
python ${CLAUDE_PLUGIN_ROOT}/scripts/translation_helpers.py strip-glossary \
--input ./glossary.md \
--output ./output/{lang}/.cleaned-glossary.mdThe .cleaned-glossary.md is a hidden working file, not part of the final output.
For each chapter in the chapters array, tokenize the XHTML to replace tags with semantic XML-style tokens:
python ${CLAUDE_PLUGIN_ROOT}/scripts/xhtml_tokenizer.py tokenize \
--input ./source/{output_filename} \
--output ./output/{lang}/.tokenized/{output_filename}.txt \
--registry ./output/{lang}/.tokenized/{output_filename}.jsonRun this for every chapter. Log any tokenization failures but continue with the remaining chapters. A chapter that fails tokenization cannot be translated -- mark it as failed in metadata.json.
Rebuild-only shortcut: Before building the translation list, check if ALL chapters already have .translated/ files on disk:
# For each chapter, check if ./output/{lang}/.translated/{output_filename}.txt existsIf every chapter has a .translated/ file already, skip the entire translation step. Log: "All chapters already translated — skipping to detokenization and EPUB rebuild." Jump directly to Step 8.
This enables fast EPUB regeneration (e.g., after a tokenizer fix) without re-running expensive translation agents.
Otherwise, build the list of chapters to translate:
chapters_translated (retry those in chapters_failed)Process chapters in batches of 8. For each batch, dispatch 8 Agent tool calls simultaneously in a single response.
CRITICAL: Dispatch all agents in the batch at the same time. Do NOT wait for one agent to finish before starting the next within the same batch. Between batches, wait for all agents to return before starting the next batch.
Each agent receives this prompt (adapted per chapter):
You are a literary translation agent for "{title}".
>
## Context - Genre: {genre} - Translating from {source_language} to {target_language} - Chapter {sequence} of {total_chapters}
>
## Files to Read Read these files using the Read tool: 1../glossary-rules.md-- your translation philosophy rules 2../output/{lang}/translation-style-brief.md-- style guidelines for this translation 3../output/{lang}/.cleaned-glossary.md-- glossary terms (USE THESE EXACTLY, no synonyms, no variation) 4../output/{lang}/.tokenized/{output_filename}.txt-- the tokenized source chapter to translate
>
## CRITICAL RULES 1. GLOSSARY TERMS ARE NON-NEGOTIABLE. Use the exact translation from the glossary. No synonyms, no creative alternatives. 2. TRANSLATE EVERY SENTENCE. No omissions. No summaries. Every sentence in the source must have a corresponding translation. 3. PRESERVE ALL XML TOKENS. The source text contains formatting tokens that look like XML tags (e.g.,<em:1>,</strong:2>,<br:3/>). These are NOT real HTML — they are placeholders for original formatting. Keep every token exactly as-is in your output, positioned correctly relative to the translated text. Do not modify, remove, translate, or invent tokens. 4. Follow the style brief for tone, register, humor adaptation, and cultural references. 5. Be faithful to the original but natural in the target language. Idioms should match existing idioms in the target language. Adapt humor, swear words, and cultural references naturally.
>
## Output Write THREE files:
>
### 1. Translated chapter Write the translated tokenized text to: ./output/{lang}/.translated/{output_filename}.txt This should be the full translated text with all tokens preserved in place.>
### 2. Translator notes Write to: ./output/{lang}/translator-notes/chapter-{seq:02d}-notes.md Format: ```markdown # Translator Notes: Chapter {seq}>
## Summary [1-2 sentence summary of what happens in this chapter]
>
## Translation Decisions [Notable choices: adapted idioms, wordplay handling, cultural adaptations]
>
## Ambiguous Passages [Any passages where meaning was unclear, with your interpretation]
>
## Warnings [Any issues: very long sentences, untranslatable wordplay, potential glossary conflicts] ```
>
### 3. New terms (only if new terms found) Write to: ./output/{lang}/new-terms/chapter-{seq:02d}-terms.md Only create this file if you encounter important terms NOT in the glossary. Format: ```markdown # New Terms: Chapter {seq}>
| Source | Translation | Context | |---|---|---| | {source_term} | {your_translation} | {brief context for why this term matters} | ```
Agent allowed-tools: [Read, Write, Edit, Bash, Glob, Grep] (No Agent nesting, no WebSearch -- translation agents just translate.)
After each batch completes, for each agent result:
python ${CLAUDE_PLUGIN_ROOT}/scripts/xhtml_tokenizer.py validate \
--original ./output/{lang}/.tokenized/{output_filename}.txt \
--translated ./output/{lang}/.translated/{output_filename}.txt python ${CLAUDE_PLUGIN_ROOT}/scripts/xhtml_tokenizer.py detokenize \
--input ./output/{lang}/.translated/{output_filename}.txt \
--registry ./output/{lang}/.tokenized/{output_filename}.json \
--output ./output/{lang}/{output_filename}chapters_translated (if passed) or chapters_failed (if failed after retries).After ALL batches complete, merge per-chapter new-terms into a single file:
python ${CLAUDE_PLUGIN_ROOT}/scripts/translation_helpers.py merge-terms \
--terms-dir ./output/{lang}/new-terms/ \
--output ./output/{lang}/new-terms.mdIf no new-terms files exist (no agents found new terms), this will produce an empty or minimal output file. This is normal.
Only proceed if at least one chapter was successfully translated.
Call rebuild_epub.py to produce the final translated EPUB:
python ${CLAUDE_PLUGIN_ROOT}/scripts/rebuild_epub.py \
"{original_epub_path}" \
"./output/{lang}/" \
"./{title_slug}-{lang}.epub" \
--target-language {target_language} \
--metadata-json ./metadata.jsonWhere {title_slug} is the sanitized book title (lowercase, hyphens for spaces, no special characters) -- the same slug used for the workspace directory name.
If the rebuild fails, log the error but do NOT crash the workflow. The translated chapter files are still available even without the EPUB.
Update metadata.json:
status to "translated"translation_progress.status to "complete"translation_progress.epub_path to the EPUB output path (if rebuild succeeded)Display a comprehensive summary:
## Translation Complete
**Book:** {title}
**Translation:** {source_language} -> {target_language}
### Results
- Chapters translated: {N}/{total}
- Chapters failed: {M} {list of failed chapter sequences if any}
- New terms discovered: {count} (see output/{lang}/new-terms.md)
### Output Files
- Translated EPUB: ./{title_slug}-{lang}.epub
- Translator notes: ./output/{lang}/translator-notes/
- New terms for review: ./output/{lang}/new-terms.md
### Next Steps
1. Review the translated EPUB in your e-reader
2. Check translator notes for flagged passages
3. Review new-terms.md and add validated terms to glossary.md
---
Translation pipeline complete!If some chapters failed, add a note:
### Failed Chapters
Chapters {list} could not be translated due to token validation errors.
You can retry them by removing those chapter numbers from `chapters_translated`
in metadata.json and running /translate-book {workspace_path} again.ebooklib, lxml, and tiktoken packages required.tokenized/, .translated/, and .cleaned-glossary.md are working files, not part of the final output~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.