@orbit-labs-ai/translator

Isolated Pi and Claude Code translation hooks with history reuse, independent controls, and display-only Chinese output

Packages

Package details

extension

Install @orbit-labs-ai/translator from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@orbit-labs-ai/translator
Package
@orbit-labs-ai/translator
Version
0.7.2
Published
Oct 27, 2026
Downloads
2,385/mo · 335/wk
Author
fhxgs
License
MIT
Types
extension
Size
1,012.8 KB
Dependencies
0 dependencies · 0 peers
Pi manifest JSON
{
  "extensions": [
    "./pi/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

Translator

English | 简体中文

License: MIT npm Node.js Integrations

Write prompts in Chinese, send English to the model, and read the answers in Chinese if you like.

Translator is a translation layer for the Pi coding agent and Claude Code. When you submit a prompt that contains Chinese prose, it translates the prompt into English in a separate, isolated request and submits the English instead. With answer translation turned on, it also translates each final answer into Chinese and shows the translation after the original. The model receives the translated prompt; original prompts and Chinese answer translations remain display-only records.

It is part of Orbit Tools.

Contents

Features

For example, when you type the first line below (the Chinese asks the agent to check a null-pointer problem in src/app.ts without touching the tests), the model receives something like the second line:

You type:          请检查 `src/app.ts` 中的空指针问题,不要修改测试。
The model gets:    Please check the null-pointer issue in `src/app.ts`; do not modify the tests.

Translation

  • Chinese in, English out (Input). A prompt with Chinese prose is translated by a separate request that sees only that prompt, and the translation is sent instead. Preflight sets aside certain literals and names; if no Chinese prose remains, including literal-only input, the prompt passes through without a translation call or record. Quoted Chinese prose remains translatable.
  • Answers in Chinese (Output). Optionally, translate final answers for display only. Claude Code also translates answers beside pending selection boxes, without waiting for the selection. Already-Chinese sections and literals are protected; a brief English preface before an otherwise Chinese answer skips. Substantive English still translates, and plain OK still translates.
  • Subagent tasks and reports. Pi single/parallel/chain tasks and Claude Code agent launches and identifiable parent-to-child follow-ups get input translation. Completed child reports get display-only translations; parent-visible tool results and chain handoffs stay unchanged. See Subagents and selection dialogs.
  • A language instruction (Instruction). Optionally, translated prompts ask the model to reason in English and answer in Simplified Chinese.
  • Faithful, minimal edits. The translator fixes only obvious typos and grammar slips; it does not rephrase, answer, summarize or expand.
  • Reuse. Submitting an identical prompt again reuses its earlier translation instead of calling a model.

Precision

  • Shared preflight masking. In both directions, one scanner identifies certain code, math, URLs, emails, filenames (including bare Chinese filenames), paths, literal-like identifiers/settings/mentions and Markdown link targets before translation. These spans are masked and restored byte for byte; ambiguous prose stays visible to the translator.
  • Structural safety, not a language audit. A response must be completed and nonempty, and every placeholder must return exactly once with protected input blocks in order. Each fresh input translation or output part gets one completion and at most one automatic retry for API/transport failures, empty or incomplete replies, or request timeouts. Explicit cancellation, local configuration/storage errors and damaged literals never trigger a retry; there is no AI audit or repair call. Translation results are not checked again for language or semantic quality.
  • Small requests. Short placeholders, and code that never reaches the translator, keep translation requests cheap.

Safety

  • Fails safe. A prompt that cannot be translated is not sent: neither the original nor a partial translation. A failed answer translation leaves the answer as it is.
  • Separate contexts. Your original prompts and answer translations are display-only copies. The model's context holds the submitted prompt and original answer, not those display records.
  • Cancel at any time. Pi: Ctrl+Alt+C. Claude Code: the Cancel translation button while a translation runs. Cancelling sends nothing and changes no setting. Interrupting the agent stops a prompt's translation, never an answer's.

Settings

  • One command. /translator opens a native settings panel with Input, Instruction, Output, Model and Thinking; subcommands such as /translator output on set each one directly. In Claude Code it is listed like your own skills, including in the desktop app's menu before a session starts.
  • Translator-only model and thinking. Translations follow your session's model by default, at their own thinking level (medium), and you can pin another model. Neither setting touches the main conversation.
  • Independent host defaults. /translator changes the current Pi branch or Claude Code session. translator --host pi … and translator --host claude … independently persist all five defaults for new sessions, including after Pi /new or Claude /clear.
  • One set of names. Both agents use the same names: Input, Instruction and Output.

Display and records

  • Both texts on screen, each once. Your prompt shows the English that was sent and the answer stays in English; Translator adds only the Chinese: your original with each translated prompt and the translation with each answer. Pi shows them in cards rendered as Markdown. Claude Code draws them under the rows, in the terminal and in its desktop app, including prompts you send while it is working, and draws them again when you resume the session or continue it under a new session ID (a branch, a fork, a move to the background).
  • Quiet status. Claude Code shows a short status at the end of the hint line under the prompt; Pi shows a toggle bar under the editor.
  • Your history keeps your words. The prompt history of both agents (Up/Down) holds the Chinese you typed, while the model reads the English.
  • Local records. Originals, translations and the text actually sent are kept on your machine in plain text, including diagnostics for failed input. Memory-only modes keep nothing on disk.

Hosts

  • Pi and Claude Code. Both share one translation core, literal scanner, input cache and set of names, and each uses its own model. Claude Code does not need Pi.
  • Updates while agents run. Claude Code reloads Translator by itself when its files change and keeps translating, even when the reload lands in the middle of a turn. A running Pi keeps the version it loaded until /reload.
  • Other hosts. A standalone command-line tool translates text before you hand it to any agent.

See the changelog for what changed in each version.

Requirements

  • Node.js 22.19 or newer, available as node on your PATH. The tool has no npm dependencies.
  • At least one host: Pi 1.0.4 or newer, or Claude Code 2.1.292 or newer; these remain the installer's minimums for ordinary translation. Pi subagent support requires the Pi 1.3.0 public tool contract. Version 0.7.2 is verified on the unmodified official Pi 1.4.0 and Claude Code CLI 2.1.311 on 2026-10-28, using isolated local model fixtures. No Pi core patch or configuration migration is required. See the 0.7.2 verification record, including auxiliary usage receipts and native Pi dialog-stack regressions. Claude uses the official Mods API; remote-render tests do not claim manual GUI acceptance.
  • Historical hosts. The 0.7.0 record retains the full Pi 1.3.0, CLI 2.1.302 and desktop-engine 2.1.299 results (app 2.42304.0). The 0.7.1 record retains its focused Pi 1.3.0 UI rerun. Pi 1.3.0, older minimums and the desktop engine are not newly retested for 0.7.2; auxiliary usage is verified only on Pi 1.4.0.
  • macOS or Linux. It has been exercised on macOS; Windows is not supported, and the installer refuses to run there. No sudo is needed.
  • A working model login in your agent. Translations run on your agent's own model, so they use your usual account; the installer itself never calls a model or touches credentials.

Quick start

0.7.2 adds Pi auxiliary usage reporting without changing native totals or your preferences. GitHub and npm publication are separate steps; local verification does not establish availability in either channel. Once published, use the exact npm version below or its matching GitHub archive as described in Upgrading from earlier versions. An unversioned npm install may select an older release.

  1. Once 0.7.2 is available on npm, install the package and run its installer:

    npm install -g --ignore-scripts @orbit-labs-ai/translator@0.7.2
    translator install

    Or clone the repository and run the installer from the clone:

    git clone https://github.com/Orbit-Labs-AI/Orbit-Tools.git "Orbit Tools"
    cd "Orbit Tools/Hooks/translator"
    ./install.sh

    The installer finds Pi and Claude Code on your machine. If only one is installed, it sets that one up; if both are, it asks which to use. translator takes the same options as ./install.sh, which you can also run by its full path from any directory; Installation in detail explains them. Keep the installed package or the cloned directory: the agents load the tool from it.

  2. Restart your agent (in Pi, /reload also works), then run /translator status to confirm that the tool loaded. In either agent's terminal UI, /translator without arguments opens its native settings panel.

  3. Type a prompt in Chinese. Input translation is on by default.

  4. Optionally, turn on the other two features for the current session:

    /translator output on         # show a Chinese translation of each final answer
    /translator instruction on    # ask the model to reason in English and answer in Chinese

To change the defaults for new sessions, use translator. It is installed in ~/.local/bin, which must be on your PATH; the installer does not edit your shell startup files.

Usage

Commands and shortcuts

/translator is the tool's only slash command. With no arguments, it opens a native settings panel with five options: Input, Instruction, Output, Model and Thinking. Changing settings makes no model call and never changes the main conversation's model or thinking level.

Action Command (Pi and Claude Code) Pi shortcut Claude Code button above the prompt
Open settings /translator — —
Show all settings / help /translator status / /translator help — —
Input: translate prompts to English /translator input [on|off|toggle|status] Ctrl+Alt+T Input
Instruction: append "reason in English, answer in Chinese" /translator instruction [on|off|toggle|status] Ctrl+Alt+L Instruction
Output: show answers in Chinese /translator output [on|off|toggle|status] Ctrl+Alt+Y Output
Model /translator model [follow|MODEL] — —
Thinking /translator thinking [low|medium|high|xhigh|max] — —
  • Both agents use the same names, defined once in shared/features.mjs: the toggles read Input: ON, Instruction: OFF and Output: OFF, full status lines read Translator: Input ON · Instruction OFF · Output OFF, and notices start with Translator · Input or Translator · Output. Claude Code's prompt hint shows the short form, such as Translator · Input + Output.
  • input, instruction and output without a value toggle that switch; their status action is read-only. model and thinking without a value only report settings. Invalid arguments leave settings unchanged.
  • For Pi, MODEL is a registry provider/model ID; the compact panel includes ON/OFF badges, inline model/thinking feedback and a searchable model picker. Press ? in the settings list for contextual help; press it again to return to the controls-only view. For native Claude Code, use an alias such as sonnet or a full model ID; the panel offers common aliases and an ID input. With Claude's optional Pi backend, use provider/model instead. Availability and allowlists for Claude's backend are checked on completion, not by a test model call when saving settings.
  • model follow explicitly overrides any model pin in the config file. It follows Pi's current session model or native Claude's current session model; for Claude's Pi backend it follows Pi's configured default, not the Claude model. Thinking remains independent, defaulting to medium; follow does not adopt the main conversation's thinking level. Pi shows both requested and effective thinking, including clamping or no reasoning support.
  • All five settings apply to the current scope: Pi saves overrides per conversation branch, Claude per session ID. They override file defaults without editing the shared config. New sessions use file defaults; persisted settings restore on resume, and Pi tree navigation restores the active branch's values. No-persistence modes keep them in memory. Changes affect subsequent translations, not the settings snapshot of an in-flight request.
  • The existing toggle rows stay synchronized with the panel, commands and shortcuts. In Pi fullscreen mode, click Input, Instruction or Output below the editor. The bar wraps without changing your draft or focus; regular mode keeps keyboard controls. RPC/JSON/print create no terminal widget or panel; the no-argument command reports status/help instead. Global defaults refresh at input/agent boundaries or reload; TRANSLATOR_DISABLE=1 visibly locks input off.
  • In Claude Code, the Input, Instruction and Output buttons above the prompt have no keys of their own: before this version, a digit typed into an empty prompt box pressed them after a short pause. Click them, or press Ctrl+X, then Tab to reach the row, then Enter; the open settings pane keeps keys 1–3. No global Ctrl shortcut or existing binding is overridden. If the row or settings pane is unavailable, use the direct subcommands. Claude serializes overlapping panel, button and command updates per session, including memory-only mode.

Pi auxiliary usage

/translator usage (Pi only) shows Translator's reported tokens and cost separately from Pi's native totals, grouped as input, output and subagent translations. Every actual isolated completion attempt has its own numeric receipt, including retries, refusals, failed validation, long-answer parts, task previews and child-report previews. Skips and cache hits add no attempts or charges. These are provider/SDK-reported values, not a billing audit or an estimate; unreported usage is unknown, not free. The command also shows pending/cancelled requests and persistence warnings. It makes no model call.

Receipts are non-context orbit.translator.usage.v1 custom entries containing only a unique request ID, provider/model, numeric Pi usage (or null), outcome and direction, with nativeAccounting: false. No prompt, answer or error text is included. Totals deduplicate request IDs across the whole session, including abandoned branches; old translation-card aggregates are not counted. Main-model totals, tool results and context are unchanged. Claude Code and the standalone CLI do not emit this Pi contract.

Persistence limits: receipts use only Pi's public session-entry API, with no new sidecar. In Pi 1.4.0, before the first conversation message is saved—including a blocked first input—they are memory-only and may be lost on exit or session replacement; a later conversation message flushes buffered entries. --no-session always stays in memory. A receipt write failure warns and keeps a best-effort in-memory total, without retrying the model call. Cancellation/timeout may still return numeric usage; while no reply has arrived, the command reports the attempt as pending. Navigation, reload or session replacement closes unresolved attempts as interrupted with usage: null on the old branch; late replies cannot overwrite those receipts or write into a new owner. Bare disposal or process termination can lose unresolved accounting. See the Pi receipt contract and limits.

Cancel pending translations

Cancellation uses a shortcut/button, not a slash command or a cancel subcommand. It does not turn any setting off: cancelled input submits neither the original nor the translation, and cancelled output leaves the completed original answer intact. Subsequent translations remain enabled according to your settings.

  • Pi: Ctrl+Alt+C cancels pending input and output translations, including when both are active or the settings panel has focus. Esc, or another abort of the run, ends Pi's wait for an answer's translation but not the translation, whose card follows; a prompt still being translated for Pi's queue when the run stops goes back to the editor. Ordinary SDK/RPC abort() during idle input preprocessing is not a reliable cancellation path. Tree navigation, reload and session replacement invalidate pending results. Bare SDK dispose() blocks late writes but may leave a request running until completion or timeout.
  • Claude Code: a Cancel translation button appears while a translation runs, in the above-prompt row and in the open settings pane (key 4 there). Click it, or press Ctrl+X, then Tab to reach the row. Cancellation reaches native completions and the worker, including its optional Pi subprocess. Once the completed result begins publication, the button disappears: cancellation does not undo completed translations or irreversible host record/display writes. Interrupting Claude (Esc, or the desktop app's Stop) stops a prompt's translation, as it stops the prompt's own submission; a prompt you typed while Claude worked stays queued and is translated when it is sent. Neither an interrupt nor a switch to another session stops an answer's translation, which runs after the turn has ended. See the Claude adapter.

What you see

  • Your prompt. The English prompt and your Chinese original, labelled Translator · Input · original · display only, appear together. Pi appends the original's card only after the translated user row is delivered, not when translation finishes or enters a queue. Claude Code keeps its existing synchronized original under the English row, plus its display-only transcript notice (Ctrl+O).
  • Prompts sent while the agent works. A prompt's card or drawing appears once the prompt is delivered, so intervening tools cannot bury it: in Claude Code under its row as soon as Claude reads it, with Ctrl+X Ctrl+S (or Ctrl+Enter) into the running turn or Enter for the next turn; in Pi after its user row, for both steering and follow-up, while the original is shown above the editor until then. Journals and enabled input caches are still written before submission, not deferred with the display.
  • Answer translations. The Chinese appears right under the English answer, labelled Translator · Output · display only: drawn under the answer row in Claude Code, in the terminal and in its desktop app, and in a card after the answer in Pi. In both, the model never reads it. Claude Code translates an answer after its turn has ended, so you can go on at once; the Chinese is drawn when it is ready, even if you interrupt or a new turn has started. Pi waits for it before ending its turn, but only until you interrupt or send another prompt; the card then follows and, landing after later rows, names the answer it belongs to.
  • Markdown in Pi's cards. Links, paths, inline code, code blocks and lists in a card are drawn with Pi's own Markdown renderer and theme. Press Ctrl+O to expand cards with the translator model and trace.
  • After a restart. Claude Code draws the originals and Chinese answers again when you resume a session, from links kept in its local journal, and when a conversation continues under a new session ID: /branch, --fork-session, the desktop app's Rewind or Fork, or ← for agents in the terminal; the Translator settings of the session it continues come along. Pi keeps both in its own session file.
  • Failures. A failed input translation blocks the prompt and immediately shows why and the original; it does not wait for a user row that will never arrive. A display failure after successful delivery cannot undo submission; Pi reports Prompt sent; original display unavailable instead. A failed answer translation shows a short warning and leaves the answer as it is.

Prompt history keeps your Chinese

Claude Code's own prompt history keeps what you typed. Translator translates your prompt, lets the text you typed enter, and rewrites only the stored prompt row into English before it is saved and sent. The model and the transcript read English, while Up/Down and Ctrl+R show your Chinese at once, with no English first, also after a restart, with the cache off and in memory-only mode. Entries an earlier release saved there in English get your originals back when Claude Code next starts: a line whose text is exactly the English of a prompt the same session translated within ten minutes of it gets that prompt's original, and every other line stays as it was, byte for byte. Translator replaces the file only under the lock Claude Code writes it under, and only while nothing was added to it meanwhile; then every Up press shows your Chinese. When Claude Code puts a sent prompt's English back in the box (Esc right after sending, or a rewind), Translator swaps in the known original as the box goes from empty to a draft, with one read and one fill; it never rewrites text you typed. Claude Code puts that text back without telling a mod first, so its English may still show for a frame. A prompt with an image that you send while Claude works is saved in English: Claude Code keeps no rewrite of such a row, so Translator sends the English itself; Up maps it back from an empty box, and the next start restores it in the file.

Sending a recalled prompt unchanged replays its saved translation, with the current language-instruction setting and without a new translation call, also with automatic caching off and under an older cache contract, provided the saved result still preserves the source's literals. Edited or retyped prompts take the normal translation path, where the cache may serve them. Turning Input off sends the prompt as typed. Translator knows the originals of this session's prompts and of your recent prompts in any session (the newest records of the input cache), so an English entry not yet restored in the file is swapped too.

Pi keeps typed prompts in its editor history too. When it refills that history from the stored rows, which hold the English (at startup, with pi -c, after /tree and on a session switch), Translator maps each translated row back to its original. When /tree or /fork puts a translated prompt back in the editor, you get its original too.

The Claude Code desktop app recalls prompts from its own list of the conversation's messages, which no mod reaches: prompts of the current session and prompts folded into a running turn recall your Chinese, but after the app reloads a conversation, prompts you sent while Claude was idle recall the English.

Default settings for new sessions

The installed translator shell command changes defaults in config.json, without calling a model. Use a host scope to avoid changing the other agent:

translator --host pi status
translator --host pi input on
translator --host pi instruction on
translator --host pi output off
translator --host pi model provider/model-id
translator --host pi thinking low
translator --host claude input on
translator --host claude instruction off
translator --host claude output on
translator --host claude model sonnet
translator --host claude thinking medium
translator --host claude cache off
translator --host pi model follow       # explicitly follow even if common defaults pin a model
translator --host pi model inherit      # remove the host pin; inherit common defaults
translator --host claude reset output   # restore inheritance for one field
translator --host claude reset all      # all known fields, including backend and timeout
translator                             # show common defaults
translator output off                  # legacy common-default command

Each host supports input|instruction|output|cache [on|off|toggle|status|inherit], model [follow|MODEL|status|inherit], and thinking [low|medium|high|xhigh|max|status|inherit]. Pi models use provider/model; native Claude accepts an alias/full ID, while Claude's Pi backend uses provider/model. Unscoped model changes the common Pi-backend pin. reset accepts all, the five settings, or cache. reset all keeps unknown fields.

Host defaults are partial overrides under hosts.pi / hosts.claude; unscoped commands retain their common-default meaning. Each invocation changes only the requested fields, under the canonical file lock, preserving symlinks, permissions, unrelated hosts and unknown fields. Existing branch/session overrides still win; resuming a session restores them. A genuinely new session, Pi /new, or Claude /clear starts from its host defaults. Slash commands remain session-only. External writers must respect the lock too.

Subagents and selection dialogs

  • Chinese task previews (Input). Chinese task prose is translated to English before delivery; English tasks are delivered byte-for-byte, without an added language instruction. Both get a Chinese task preview: existing Chinese is retained and English prose is translated for display only, even when Output is off. Mixed tasks retain their original Chinese sections. This may add a separate display completion; literal-only tasks still skip. The short-English-preface exemption for answers does not apply to task instructions. Display-translation failure warns and retains the valid English task; cancellation still prevents undispatched work. Your own prompt history keeps exactly what you typed, in either language; generated tasks/previews never become human recall entries.
  • Pi 1.3.0 / 1.4.0: the parent translates the documented subagent tool's task, every tasks[].task, or every chain[].task before dispatch. A failed Chinese-to-English conversion, recording failure or cancelled preparation blocks the whole batch. Agent names, directories, trust flags and {previous} operators stay intact; the chain receives original previous results. Completed successful child answers appear in labelled cards. Unknown custom shapes, invalid batches and parallel batches outside the supported 1–8 task limit are blocked before preview model calls while Input is on, not guessed. An explicit task-hash handoff coordinates matching direct JSON children without suppressing unrelated child sessions or retranslating chain results. With Input on, a chain task must contain fixed non-whitespace task text outside {previous}; placeholder-only tasks are blocked rather than treated as wildcard ownership. See Pi's supported contract.
  • Claude Code: agent.spawn handles foreground/background/nested agents and teammates; session.send handles identifiable parent-to-child follow-ups, including resumed children. Completed, non-aborted reports translate for child rows, matching Agent results or background notifications. Other peer messages and child-to-parent delivery remain unchanged. Unsupported, non-rewritable workflow prompts fail closed; remote children without local completion events cannot display a local translation. See Claude's task contract.
  • Selection boxes: an answer whose tools are only AskUserQuestion / ExitPlanMode translates while the selection waits. Question text/options receive an annotation above the unchanged native question, using the official dialog render contract. Approval controls, option values and the user's answer are never rewritten. Ordinary operational-tool chatter still skips.
  • Interruption and display: Cancel stops translation, not already-dispatched children. Failures retain original answers/results. Parallel identities, repeat events and reloads are de-duplicated; completed task/selection drawings restore from journals. Pi navigation/reload invalidates pending work; Claude's completed-answer jobs can outlive a turn or session switch. Quitting still ends unfinished work. No tool result is replaced, and no display translation enters parent/child model context.

How translation works

Your prompt
  |-- input off --> sent unchanged
  `-- shared preflight scanner: identify certain literals and inspect remaining prose
       |-- no Chinese prose / literal-only --> unchanged, no translation call or record
       `-- Chinese prose
            |-- exact source + current scanner/prompt contract cached --> literal-integrity reuse
            `-- mask identified literals --> isolated completion (at most one API retry)
                   --> completed/nonempty response + placeholder count/block-order safety
                         |-- failure --> prompt blocked (no repair call)
                         `-- restore literals --> record + optional language instruction
                                                   |
                                     Translation sent to the model; your original
                                     is shown beside it, display only

The model's final answer ----------------------------------> left unchanged
  |-- output off --> no translation
  `-- shared preflight scanner
       |-- no English prose / literal-only --> no translation call or record
       `-- English prose --> mask literals --> completion per part (at most one API retry) + response/literal safety
                                                --> restored translation shown/stored only

A failed input translation blocks the prompt: the tool never falls back to sending the Chinese original. To send the original on purpose, run /translator input off first. A failed answer translation keeps the original answer: nothing is rewritten and the model is not asked again.

Local language detection

Language decisions happen only before translation, locally and before a translation call or record. Both directions use the same preflight scanner to identify certain literal spans and inspect the remaining prose. If translation is needed, those same spans are masked before the request; filenames and literal-like names are not merely ignored by detection and then exposed to the model.

  • Ordinary prompt input is sent as typed when no Chinese prose remains. Read 报告.md costs no translation call, and 报告.md alone now skips too. Quoted Chinese prose is still translatable: Change the title to “你好” takes the translation path, and the model may keep an exact quoted value. Quotes alone do not make arbitrary prose a protected literal.
  • Output deliberately favors avoiding repeat translation. In addition to the conservative scanner, it masks ASCII filenames/paths even when glued to Chinese (已保存README.md文件), contextual acronyms/identifiers and the exact labels Markdown, Mermaid, Downloads, Token and Tokens. A single Titlecase name directly between Han prose, allowing spaces (已更新 Orbit 的文档), is treated as a name. These are real protected spans, not just gate exceptions. 已完成 API checks still translates; ordinary standalone OK, Thanks and Mermaid still translate.
  • Already-Chinese sections are not sent again. Source units split only at unprotected line/sentence boundaries. Units with no English prose are masked and restored byte for byte alongside literals; adjacent English remains batched, not one model call per line. A mixed sentence with substantive English remains eligible. If the only English is a leading preface of at most 12 words before an otherwise Chinese body with at least 80 unmasked Han characters, the whole answer skips. This intentionally tolerant display policy can leave a short English preface visible. It is not a general Chinese-majority exemption: an English conclusion or a substantial English section still translates, without resending the Chinese body.
  • Literal-only text skips in both directions, even without any prose in the other language. Examples include 报告.md, ./文档/报告.md, 用户表_v2, LABEL=中文 and @张三. Skipping makes no translation call or translation log and adds no language instruction.

Known technical acronyms such as API remain protected in Chinese output, but uppercase instructions such as DO NOT DELETE BACKUPS stay visible for translation. A path-like token such as ./old.ts改为new.ts keeps its intervening Chinese instruction visible; wrap an actual filename of that form in backticks.

For input, ambiguous prose remains visible to the model rather than being treated as one protected name: Chinese glued straight to Latin letters (请查看data.csv, …/docs然后) or tied to a name by : or 、 (问题:src/app.ts, 配置、README.md), a link or path that ends in Chinese without a file extension (https://example.com/文档里的第三节, D:\备份里面), a mention that runs on into a sentence (@李四麻烦今天合并一下), an address with a Chinese local part, and a join by slashes or # alone (English/中文, src/组件/按钮, 问题#3). A doubtful case costs a translation rather than a missed one. Explicit, hard-ended paths such as ./src/组件Panel.vue and C:\项目\My文档.md are protected as a whole, including mixed-script segments. Output also protects hard-ended multilingual relative paths next to Chinese prose, such as 已保存src/中文Panel.vue并完成验证。, rather than exposing only their ASCII tail. Exact strings outside the recognized syntax, including ambiguous names or standalone symbol sequences, can be wrapped in backticks for protection.

请检查 报告.md is translated with the filename masked and then restored. Neither the draft nor the restored text is passed through a language gate afterward; literal content cannot trigger a language rejection.

Which model translates

Without a configured or session pin, translations use the model currently selected in your session, read again for every request, so switching the main model also switches the translator. They run at the translator's thinkingLevel (default medium) rather than your session's own level: a session at max still translates at requested medium unless you change the translation setting.

  • Pi clamps the requested level to what the translation model supports; a model without reasoning gets no reasoning option. The panel shows the selected/resolved model inline and requested → effective thinking only when they differ; select Thinking and press ? for the reason. /translator status retains the full details. Pin with /translator model provider/model, or set provider and model as file defaults. An unavailable pin does not silently fall back to the main model.
  • Claude Code uses its own isolated completion API by default. Pin with /translator model sonnet (or a full ID), or set the claudeModel file default. Requested thinking is sent as effort; the host determines model support and omits it for models that take none. Alternatively, claudeBackend: "pi" uses an installed Pi and accepts a provider/model pin.
  • /translator thinking high changes thinking for both translation directions only. /translator model follow clears the effective translation pin for the current branch/session even if the file has a pin; it leaves thinking unchanged. Neither command changes the main model, its thinking level or the shared config file.

Small fixes, no rewriting

The input translator may make minimal corrections to obvious spelling, grammar or typing mistakes in prose. It completes a fragment only when the missing words are unambiguous from the original; otherwise the fragment or ambiguity stays as it is.

It must not rewrite, polish, expand or guess. Meaning, tone, detail, negation, constraints, lists and Markdown are kept. These rules apply only to translated input: English prompts still pass through untouched, and answer translation is unaffected. They are instructions to the model, not a guarantee about any particular model's quality.

What is protected locally

The shared scanner's input boundaries remain conservative so prose is not hidden inside a guessed name. Output adds the tolerant spans and already-Chinese sections described above; detection and actual request masking agree. The table describes the common conservative base. Neither pass is a complete parser for arbitrary source text.

Masked locally Examples and boundaries
Code Backtick and tilde fences (also blockquoted and unclosed ones), inline code with an exact backtick run, <pre>, <code>, <script> and <style> blocks. Chinese inside code stays as written
Math $…$ (one line, not $5 to $10), $$…$$, \(…\), \[…\]
URLs and emails ASCII URLs (https://…, www.…, mailto:) and ASCII email addresses. 请阅读https://…,然后 masks the URL alone; a URL that runs straight into Chinese, as in 看看https://a.com/docs然后, is left whole to the translator
Filenames and paths Certain bare filenames and relative paths (README.md, 报告.md, docs/报告.md), explicit paths (/, ~/, ./, ../, C:\, \\server, @src/…), and quoted paths or filenames with spaces or Chinese. Ambiguous trailing Chinese remains prose; a file extension or closing path separator provides a clear boundary. A / right after Chinese starts an ASCII path only when another / follows, so 项目/Orbit stays prose
Literal-like identifiers, settings and mentions Certain names such as 用户表_v2, LABEL=中文 and short mentions such as @张三; not arbitrary mixed prose, slash-separated phrases or a mention followed by a whole sentence
Markdown links Link and image destinations, reference definitions and identifiers, footnote identifiers and wikilinks; link labels and titles are prose

Not protected as a whole: ambiguous names and paths, JSON, ordinary HTML tags and attributes, arbitrary command-line values, quoted prose, indented text and formulas without delimiters. Recognized literals inside them may still be masked. The translator is instructed to keep exact values and translate prose, but those instructions are not a byte-integrity guarantee for unmasked text.

After completion, only response and restoration safety is checked locally:

  • Completed, nonempty response. Incomplete, empty or failed responses are rejected. Native Claude also requires its unique terminal marker before accepting a response.
  • Every placeholder comes back exactly once. Placeholders are short (__PT_ and 8 random hex digits, never present in your text) to keep token use low. Input word order may move an inline literal; protected fenced code blocks keep their relative order. A display-only answer translation may move any literal. Exact source bytes are restored only after these checks.
  • One API retry, no AI audit or repair call. API/transport failures, empty/incomplete replies and configured request timeouts get at most one retry per input or output part. Explicit Cancel, local configuration/storage faults and placeholder failures do not retry. A final failure blocks input or retains the original answer. Refusal fallback and retry share the same two-attempt budget; see Configuration.

There is no post-translation language or semantic check: input is not rejected for remaining Chinese, and output is not required to contain Han characters. This also applies to restored literals and joined long-answer parts. Literal content cannot cause language rejection. A structurally accepted result may still be untranslated, inaccurate or otherwise poor; Translator makes no semantic quality guarantee.

For example:

请阅读https://claude.dev/blog/getting-started-with-claude-code-mods/,然后告诉我mods的工作原理

The URL is masked; the translator sees 请阅读__PT_…_0__,然后告诉我mods的工作原理 and returns Please read __PT_…_0__, then tell me how mods work. Images go to the main model unchanged and are never sent for translation; there is no OCR or translation of attachments.

Pi and the shared worker use shared/literals.mjs. Claude's sandbox uses a generated, byte-identical mirror in claude-code/hooks/literals.js; it cannot import outside the plugin. Run npm run sync:shared after scanner changes, and npm run check:shared to check without writing. Do not maintain two algorithms.

Long answers

A translation request takes longer the more it has to write. So in Pi, in Claude Code (both backends) and in the command-line tool, an answer longer than 2,000 characters once literals are masked is translated in parts of about equal length, up to four at a time: a 7,000-character answer takes about as long as a 2,000-character one. A timeoutMs you set bounds each part separately.

  • Parts are cut between paragraphs, or between lines inside a block too long for one part, never inside masked code. A single line longer than 2,000 characters stays whole. The Chinese is joined back in order, with the original spacing and indentation.
  • Each part needing translation gets one completion, plus at most one API retry (with a fresh deadline per attempt when timeoutMs is set), a completed/nonempty response requirement and exact placeholder restoration checks. Preflight keeps parts with no English prose, including literal-only parts, without a request. Neither a part nor the joined answer is rechecked for language, and there is no audit or repair call.
  • If a part fails, the others stop, a warning names the part, for example Translation timed out (part 2 of 4), and the answer stays as it is. Cancelling stops every part.
  • Each part is translated without the others, so a term may be worded differently in two parts.
  • In Claude Code a part cut short is asked again, of another model family, by itself; the parts already translated are kept. Without timeoutMs a request has no deadline of its own, though the provider still ends a single reply after about ten minutes.

Translation reuse

  • A cached translation is reused only for exactly the same original text under the same scanner/prompt contract. Whitespace, line breaks and punctuation count; there is no fuzzy matching.
  • Only results accepted by response and literal-restoration checks are cached, never incomplete, cancelled or failed ones. The 0.7.0 scanner contract bump isolates older versioned entries without deleting their history. A cache entry is not a semantic quality endorsement.
  • Pi and Claude Code share the cache stored next to the configuration file. Changing the main model, translator model, translator thinking or backend does not clear or bypass it: the same valid cached input can still be reused. To compare models with fresh translations, turn the cache off.
  • The language instruction is added after a cache hit, never twice. Reused records are marked reused and do not count usage again.
  • Pi reuses records from the active branch only, not abandoned branches. The prompt history of both agents keeps what you typed, as described in Prompt history keeps your Chinese.
  • Older, unversioned Pi records can still be imported or considered from the active branch. Every nonempty candidate must retain the exact bytes and occurrence counts of source literals identified by the scanner; this is source-literal integrity only, not an order, language or semantic recheck of the cached text. A literal-changing candidate triggers a fresh translation, whose accepted result can replace it without erasing old journal records. To force a fresh translation, turn the cache off.

Configuration

Settings live in config.json in the tool directory, or in ~/.agents/translator/ for an npm install. The installer creates it from config.example.json, and Git ignores it. Without the file, these built-in defaults apply:

{
  "enabled": true,
  "cacheEnabled": true,
  "appendLanguageInstruction": false,
  "outputEnabled": false,
  "provider": null,
  "model": null,
  "claudeBackend": "native",
  "claudeModel": null,
  "thinkingLevel": "medium",
  "timeoutMs": null
}
Field Type and default Effect
enabled boolean, true Input translation. When off, the original is sent unchanged, without the language instruction
cacheEnabled boolean, true Automatic input-translation reuse. Off does not delete history or prevent explicit replay of an unchanged recalled Claude prompt
appendLanguageInstruction boolean, false Add the language instruction to translated or reused input
outputEnabled boolean, false Translate final answers into Chinese for display
provider, model non-empty string or null, both null Model override for the Pi backend; set both, or leave both null
claudeBackend "native" or "pi", "native" Claude Code translates with its own completion API, or with an installed Pi
claudeModel non-empty string or null, null Claude Code translation model. null follows the session's current model, including /model switches; an alias or full ID pins that model, subject to Claude's model allowlist
thinkingLevel "low", "medium", "high", "xhigh" or "max", "medium" Thinking level for translations, independent of the session's own: effort in Claude Code, reasoning level in Pi
hosts object, absent by default Optional pi and claude partial overrides using the same fields above/below. Model/provider pairs must be set or reset together; unknown fields are preserved
timeoutMs null or an integer from 1 to 120000, null null sets no deadline: a translation runs until it finishes or you cancel it. A number is the deadline in milliseconds for each translation request, including start-up time for the Pi subprocess backend; a request past it fails with Translation timed out. Each part of a long answer is a request of its own

For example, "hosts": { "pi": { "appendLanguageInstruction": true, "outputEnabled": false }, "claude": { "appendLanguageInstruction": false, "outputEnabled": true, "claudeModel": "sonnet" } } can be added to the existing configuration object. Existing flat files need no migration. The standalone translation CLI uses common defaults, not either host layer.

Settings are applied in this order, later ones winning: built-in defaults, common top-level configuration, the matching hosts.pi or hosts.claude layer, branch/session overrides for the three switches plus translator model and thinking, and finally TRANSLATOR_DISABLE=1, which turns input translation off. Unset session fields retain file defaults; explicit model follow clears the file's model pin for that scope. The file is read for every request, so edits take effect without a restart. Code updates reach Claude Code by themselves and Pi at /reload (see Moving or updating your clone); registration changes need a restart.

The Pi provider and model override changes only the model used for Pi translations. The standalone command-line tool and Claude Code's Pi backend have no Pi session model, so they use Pi's global default model or the file override; Claude's Pi backend can also use a session provider/model pin. They pass thinkingLevel as --thinking. Claude Code's native backend ignores Pi model defaults, uses its effective claudeModel (including a session pin or explicit follow), and never reads Pi's configuration or credentials. With the Pi backend, Pi manages its own login. The installer does not check model logins, so confirm that your agent itself works after installing.

Claude Code's native translations run without tools or history, at thinkingLevel effort, with a 32,000-token output limit that thinking shares (a model's own lower limit, when Claude Code names it, instead). Because the API does not report why a reply stopped, each request asks for a unique end marker, and a reply without it is never used, so a truncated translation never reaches the model. A reply without the marker was cut short: by the output limit, or by the model's safeguards, which since 2026-10-14 stop some Opus 5.5 translations partway, even of ordinary prompts. Translator then sends the same masked request once more to another model family (Sonnet, or Haiku when the translator already is a Sonnet model), and that answer is final; for a long answer, only the parts cut short are asked again. A reply the model could not give at all, because it was rate-limited, overloaded or unavailable (HTTP 429, 5xx or 404) or answered without text, gets the same single fallback. Request timeouts and other API/transport failures, including empty replies containing only the end marker, use that same single retry. Pi retries these response failures once on the same model, reserving the alternate family for refusals. These are at most two Translator-level completion attempts per part; Claude may internally retry HTTP transport calls, and lowering a rejected native token cap consumes the same budget. Explicit cancellation and local configuration, persistence or literal-integrity errors never retry. The journal records the fallback model as model, also when only some parts of a long answer needed it, the first one as fallbackFrom, and every reply in usage. In Pi, a reply its provider reports as a refusal is asked once of the newest model of another family from the same provider. Claude Code's Pi backend and the command-line tool ask the other family by name (sonnet, or haiku after a Sonnet model) at the provider that answered, which Pi resolves to a model; Claude Code's journal records it as above. The marker is removed before anything is shown or cached. A very long prompt may fail to translate on both models; split it or use the Pi backend. A partial translation is never submitted.

The language instruction

The instruction is fixed:

Use English for intermediate reasoning. Write your final response in Simplified Chinese.

It is added only to translated or reused input, never to prompts skipped by preflight (no Chinese prose, including literal-only text) or input with translation turned off. In the records, translated holds the translation alone and submitted holds what was actually sent, including the instruction. The instruction does not change the thinking level or ask the model to reveal hidden reasoning.

Environment variables and plugin options

Variable or option Purpose
TRANSLATOR_CONFIG Absolute path to the configuration file. It also moves the input cache and the Claude Code session records, which are kept next to it
TRANSLATOR_DISABLE=1 Turn input translation off regardless of session switches; answer translation is unaffected
TRANSLATOR_PI_BYPASS=1 Pi only, 0.6.8+: skip the entire Pi extension for this process, before configuration or hook registration; intended for gateway/automated runs
TRANSLATOR_NO_PERSIST=1 Claude Code: write none of this tool's history, session records or settings store, and keep all five session settings in memory. Use it together with Claude Code's --no-session-persistence
TRANSLATOR_PI Path to the Pi executable, used only by the command-line tool and Claude Code's Pi backend
PI_CODING_AGENT_DIR Pi's configuration directory; the installer honors it
CLAUDE_CONFIG_DIR Claude Code's configuration directory; the installer honors it
HOME Base for the installed links, the translator command, the default agent directories and an npm install's data directory
Claude Code plugin option bridgePath Empty by default, which means the worker in the plugin's own directory, claude-code/bridge.mjs, so each copy of the mod runs its own worker. Set an absolute path to the worker for a custom installation

To set bridgePath (the plugin ID depends on how the plugin was installed):

printf '%s\n' '{"bridgePath":"/absolute/path/to/translator/claude-code/bridge.mjs"}' |
  claude plugin configure translator@skills-dir --values-stdin

Automated Pi runs

A gateway or another unattended launcher can set TRANSLATOR_PI_BYPASS=1 only in its Pi child's environment. Translator then registers no input/output hooks, controls or renderers: prompts and answers pass through without translation, a language suffix, translation model calls or new translation records. Invalid translation config, unavailable translation models and saved branch switches cannot block or delay that run. Other Pi extensions, tools, authentication and project trust remain unchanged.

TRANSLATOR_PI_BYPASS=1 pi --mode json "Run the saved task"

This is an explicit, process-wide opt-out, not a fallback after translation fails. Do not export it globally if you want translation in normal Pi chat. Without the exact value 1, existing behavior remains, including fail-closed input in JSON/print/RPC mode; prompt wording never opts out. Claude Code and the standalone translation CLI are unaffected. Unlike this bypass, TRANSLATOR_DISABLE=1 only disables input translation. A launcher must set the new variable and use a Translator version that supports it; updating only one side is insufficient. No shared settings file or existing session record is changed.

Data and privacy

Isolated does not mean offline. Each translation request is sent to your configured model provider, and it contains the text being translated, with code and other protected literals masked. It never includes conversation history, arbitrary tool results, hidden reasoning or images. Subagent support sends only the selected task or completed public report, not the child's full conversation. Task originals share the input cache but never supply human prompt-history restoration or composer recall. Your original answer always stays in the main conversation; its Chinese translation is only shown and stored.

The tool keeps plain-text records on your machine:

Location Contents
history/input.jsonl, next to the configuration file Successful translations (original, English, rule version and source), shared by Pi and Claude Code
claude-sessions/<session-id>.jsonl, next to the configuration file Claude Code write-ahead records for input and answers: translations, submitted text, the translating model and thinking level, usage, request IDs, and which transcript row shows each translation
Pi's own session file Entries the model does not read: bilingual records, branch settings (switches, translator model and thinking), and numeric auxiliary usage receipts (subject to the first-input persistence limit)
prompt-translations/ and output-translations/, next to Pi's session files This tool's own write-ahead journals and results; Pi's session file is never written to directly
Claude Code's own transcript Bilingual notices, which the model never reads (they are not system prompts)
Claude Code's private plugin store The three switches, translator model and thinking overrides, kept per session ID
Claude Code's own prompt history (history.jsonl in its configuration directory) Claude Code's file, which keeps the prompts you type, as it does without Translator; since 0.6.16 it holds your Chinese rather than the English. Translator changes only lines that hold a translation's English, back to your original (see Prompt history keeps your Chinese), and notes the last line it read in claude-sessions/.history-<hash> next to the configuration file
translator-leases/ in the system's temporary directory Claude Code: a lock file holding a process ID while an answer is translated, and an empty marker once it is, so that two copies of the mod never both translate it. Their names are hashes, they hold no text, and they are removed after a day

New log directories are created with mode 0700 and files with 0600, and every append is flushed to disk. The default locations are ignored by Git; if you point the configuration or logs into another repository, add your own ignore rules.

To keep nothing on disk:

  • Pi: --no-session keeps records and the cache in memory only.
  • Claude Code: use --no-session-persistence and TRANSLATOR_NO_PERSIST=1; a mod cannot see the former command-line flag. Claude Code's translation requests travel between the plugin and its worker only in memory and through standard input, never as extra files. Only the answer leases above, which hold no text, still use the system's temporary directory, and Claude Code's prompt history is left as it is.

A started record with no result means the translation was interrupted. translated marks a result accepted by response and literal-restoration checks, not proof of its language or semantic quality or of the main model's whole turn succeeding. Deleting a session in your agent does not delete these records or the cache, and neither upgrading nor uninstalling removes them; delete them yourself using the table above.

Keeping translations out of the model's context does not sandbox the files: if you ask an agent to read the local logs, their contents enter that request like any other file.

To import records from older Pi sessions:

HOOK="$HOME/.agents/hooks/translator"
node "$HOOK/import-history.mjs"                    # defaults to ~/.pi/agent/sessions
node "$HOOK/import-history.mjs" "/path/to/sessions"

Importing is idempotent and never modifies the original sessions. Deleting the shared history/input.jsonl does not remove older records from Pi's active branch; to force fresh translations, run translator cache off.

Installation in detail

Which agents are set up

Agents found What the installer does
Pi only Sets up Pi, without touching any Claude Code directory
Claude Code only Sets up Claude Code; its default backend does not need Pi
Both Asks: 1 Pi, 2 Claude Code, 3 both, 0 cancel
Neither Exits with code 1 and creates no configuration, links, backups or directories

The installer looks for executables on your PATH, plus Pi at ~/.pi/agent/bin/pi and Claude Code at ~/.local/bin/claude or ~/.claude/local/claude. It checks each selected agent's version with --version; a configuration directory alone does not count as an installation. Older or unrecognized versions are refused, and an agent you did not select is neither installed nor upgraded.

Options

./install.sh --agent pi                 # Pi only
./install.sh --agent claude             # Claude Code only
./install.sh --agent both               # both, which must both be installed
./install.sh --yes                      # both if available, otherwise the one found
./install.sh --agent both --dry-run     # print the plan without writing anything
./install.sh --help

# Another Claude Code account, keeping the default account unchanged
./install.sh --agent claude --claude-dir "$HOME/.claude-work"

# A non-default Pi configuration directory
./install.sh --agent pi --pi-dir "/absolute/path/to/pi-agent"

Without a terminal to ask in, and with both agents installed, you must pass --agent or --yes; the installer does not guess. --yes does not skip conflict or version checks.

Configuration directories are chosen in this order: the --pi-dir and --claude-dir options, then PI_CODING_AGENT_DIR and CLAUDE_CONFIG_DIR, then the defaults. To use a configuration file elsewhere, set TRANSLATOR_CONFIG to an absolute path, and set it for every later agent process too.

What the installer changes

  1. Before writing anything, it checks Node, the agent versions, that the source is complete, and every destination. Any conflict stops the whole installation, so one agent is never left half set up.

  2. It creates a stable link to the source and the translator command:

    ~/.agents/hooks/translator  ->  the translator directory in your clone
    ~/.local/bin/translator      ->  ~/.agents/hooks/translator/control.mjs
  3. It creates config.json from config.example.json, with mode 0600, only if no configuration exists. Existing settings, history and logs are never overwritten.

  4. Pi: it adds <stable link>/pi/index.ts to the extensions list in Pi's settings.json, keeping all other fields and extensions. Known registrations of the old pi.ts entry are migrated and de-duplicated.

  5. Claude Code: it creates the link <claude-dir>/skills/translator -> <stable link>/claude-code. It does not change your settings hooks or replace an existing plugin with the same name.

  6. Before changing Pi's settings, it saves a byte-for-byte backup named settings.json.translator.bak.<timestamp>.<random>. File permissions and existing symlinks are preserved.

  7. Reinstallation is idempotent. File updates lock canonical targets and Pi's logical settings path, then recheck the baseline immediately before replacement. Detected changes abort installation. A failed rollback retains the original backup and reports its recovery path rather than claiming full restoration.

Locks coordinate cooperating writers, not arbitrary editors; the final comparison and rename are not an OS compare-and-swap. Avoid concurrent host-setting changes, especially first-time creation or access through unknown symlink aliases. A lock wait times out after five seconds. After a crash, inspect the reported .lock and confirm no writer is active before removing it; the installer never steals unknown locks.

Moving or updating your clone

Agents load the tool through these links, so do not delete the clone after installing. To move it, point ~/.agents/hooks/translator at the new location, update any registration or environment variable that names the old absolute path, then reload Pi or restart Claude Code. Move your config.json, history and logs along with the clone; data stored at custom external paths can stay where it is. The installer refuses to replace an existing real directory at the link's location, or a link to another clone: back up your configuration and data and resolve that by hand first.

Updates reach running agents. Agents load the tool straight from the clone, so whatever the clone holds is live:

  • Claude Code reloads the mod by itself when its files change: when the current turn ends, or sooner if you run /translator. Translation continues across the reload. A turn in progress still gets its answer translated once and drawn, and settings and pending originals are kept. If Claude Code prints reload failed, the previous version stays loaded, the new files have an error: the previous version keeps translating, and the next change to the files is tried again.
  • Pi keeps the version it loaded until you run /reload or restart it. Pi lets only commands reload extensions, so it cannot pick up an update by itself; it never mixes two versions.
  • A checkout that switches branches is not a stable install. Agents load whatever the linked clone holds, so checking out another branch, such as another tool's branch of this repository, changes every running agent with it. For everyday use, install the released npm package, which keeps its own copy outside Git (see Installing from npm), and load a development checkout only for testing, with claude --plugin-dir or pi -e as in Development and testing.

If Claude Code reports The loaded translation mod is out of date, a mod from before 0.4.0 is still loaded: restart Claude Code.

Installing from npm

npm install -g @orbit-labs-ai/translator places the tool in npm's global package directory and adds the translator command. translator install runs the same installer as ./install.sh and takes the same options; every other argument controls the defaults, as described above. The ~/.agents/hooks/translator link then points into npm's global directory.

npm replaces the package directory on every update, so an npm install keeps config.json, the translation cache and Claude Code's session records in ~/.agents/translator/ instead of beside the source.

  • Updating: run npm install -g @orbit-labs-ai/translator@latest, then run /reload in Pi. Claude Code reloads the mod by itself; if a session still runs the old version, run /reload-plugins there. The links stay valid, so there is no need to run the installer again.
  • Switching Node.js installations, for example with a version manager, moves npm's global directory. Remove the ~/.agents/hooks/translator link, install the package for the new Node.js, and run translator again.
  • Do not use npx: it runs packages from a temporary cache, so the installer refuses to run there.

Upgrading from earlier versions

From 0.7.0 or 0.7.1 to 0.7.2. Pi gains /translator usage and rejects malformed/oversized subagent batches before translation. Translation rules, preferences, cache contracts and Claude runtime behavior are unchanged. No Pi core patch, cache reset or configuration migration is needed. New receipts begin with actual attempts after reload; old translation records are not backfilled. Read the receipt persistence limits, especially for blocked first input.

Once the GitHub Release is published, download its packed package (requires GitHub CLI access), then install it in a fresh stable prefix, not over an active copy:

gh release download translator-v0.7.2 --repo Orbit-Labs-AI/Orbit-Tools \
  --pattern orbit-labs-ai-translator-0.7.2.tgz --dir /path/to/downloads
npm install -g --prefix "$HOME/.local/share/orbit-tools/translator-0.7.2" \
  --ignore-scripts /path/to/downloads/orbit-labs-ai-translator-0.7.2.tgz

After npm publication, the equivalent version-pinned install is:

npm install -g --prefix "$HOME/.local/share/orbit-tools/translator-0.7.2" \
  --ignore-scripts @orbit-labs-ai/translator@0.7.2

The package is under that prefix's lib/node_modules/@orbit-labs-ai/translator. For a new installation, run its install.sh. For an existing npm installation, finish/cancel translations, back up and deliberately repoint only the existing ~/.agents/hooks/translator symlink and CLI link to that package; the installer correctly refuses a conflicting old target. Keep ~/.agents/translator/ and existing Pi/Claude registrations unchanged. Reload Pi and Claude plugins or start new sessions; retain the old package while any session uses it. This local package installation does not publish to npm.

Clone installs have a different data location. Do not apply the npm-only repointing procedure while config.json, history/ and claude-sessions/ still live beside the old source: a node_modules installation instead defaults to ~/.agents/translator/ and would no longer find those records. Keep the old source/data, back them up, and explicitly migrate them before switching, or retain a source-based installation with its existing data paths. Preserve any custom TRANSLATOR_CONFIG path in every host. Do not overwrite another installation's data or silently start with empty defaults.

From 0.6.x to 0.7.0. No installer rerun or configuration migration is needed; existing flat defaults and session settings remain valid. Configure independent defaults with translator --host pi … / --host claude …. Use Pi 1.3.0 or 1.4.0 for subagents. After updating, run Pi /reload and Claude /reload-plugins (especially in the desktop app), or start new sessions. Finish or cancel active translations before updating: old in-flight continuation contracts can be rejected safely, without submitting partial input. The scanner contract invalidates automatic reuse of older versioned cache entries without deleting logs. For the released package, run npm install -g --ignore-scripts @orbit-labs-ai/translator@0.7.0, then reload your hosts.

From 0.6.0 (prompt-translate) to 0.6.1 (translator). Everything was renamed, and the installer does not remove the old entries. Do this once, then restart Claude Code and reload Pi:

  1. Remove the old entries: the ~/.agents/hooks/prompt-translate and ~/.local/bin/agent-translate links, the skills/local-translation link in each Claude Code account, and the …/prompt-translate/pi/index.ts entry in Pi's settings.json.
  2. Get the new version. Clone: git pull, then move config.json, history/ and claude-sessions/ from Hooks/prompt-translate/ to Hooks/translator/; Git does not move ignored files. npm: npm uninstall -g @orbit-labs-ai/prompt-translate, npm install -g @orbit-labs-ai/translator, then move the contents of ~/.agents/prompt-translate/ to ~/.agents/translator/.
  3. Run the installer again: ./install.sh in the clone, or translator install.
  4. Rename any environment variables you set: AGENT_TRANSLATE_CONFIG, AGENT_TRANSLATE_DISABLE, AGENT_TRANSLATE_NO_PERSIST and AGENT_TRANSLATE_PI become TRANSLATOR_CONFIG, TRANSLATOR_DISABLE, TRANSLATOR_NO_PERSIST and TRANSLATOR_PI. The old names are no longer read.

Commands changed too: /translator replaces /translate, /translate-language, /translate-output, /translation-status and /translate-cancel, and the translator command replaces agent-translate (agent-translate language is now translator instruction). Pi's saved branch settings still apply. Claude Code keeps session overrides per plugin name, so those saved under local-translation are not carried over, and resumed older sessions start from your file defaults. The changelog lists every change.

Older versions:

  • Pi's entry point moved from the old top-level pi.ts to pi/index.ts, and all Pi code now lives in pi/; no compatibility shim is left at the top level. The installer migrates old paths in the Pi settings it manages; update other manual or project-level registrations and external callers yourself.
  • Claude Code's worker moved from the top level to claude-code/bridge.mjs. The default plugin settings need no change, but update any custom, non-empty bridgePath. The cli.mjs, control.mjs and import-history.mjs paths are unchanged.
  • Claude Code now defaults to claudeBackend: "native". To keep translating through Pi, add "claudeBackend": "pi" to your configuration; the installer never overwrites an existing choice.

Command-line tool and other hosts

A standalone command translates text before you hand it to any agent. It needs Pi: Claude Code's native backend is only available inside its plugin. The Chinese sample means "Please check the code":

HOOK="$HOME/.agents/hooks/translator"
printf '%s' '请检查代码。' | node "$HOOK/cli.mjs"
printf '%s' '请检查代码。' | node "$HOOK/cli.mjs" --json
node "$HOOK/cli.mjs" --help

In plain mode, the text to submit goes to standard output and a bilingual preview to standard error. In JSON mode, standard output holds only {status,text?,error?,usage?}. Success, including a local pass-through, exits with 0; failure or cancellation exits non-zero with no text to submit. Callers must check both the status and the exit code instead of piping the output straight into an agent. The command adds no language instruction, keeps no session history and does not translate answers.

To support another host, call translatePrompt and translateOutput from shared/core.mjs and supply your own isolated completion and storage. An input failure must block submission, and answer translations must never be written into the model's context. The architecture guide documents the Claude Code worker protocol and the dependency rules.

Troubleshooting

Symptom What to do
The installer cannot find an agent Check your PATH, file permissions, and pi --version / claude --version. A configuration directory alone is not an installation
The installer reports a conflict Inspect the existing file, directory or link instead of overwriting it. Back it up, then decide what to keep or migrate
Pi still refers to the old pi.ts Run the installer again with the --pi-dir you actually use, and check project-level or command-line registrations
Claude Code cannot find the worker The worker is now claude-code/bridge.mjs; check for an old, non-empty bridgePath
Old /translate… commands, a local-translation plugin or a Pi error about prompt-translate after upgrading Remove the 0.6.0 entries as described in Upgrading from earlier versions, then restart Claude Code and reload Pi
A translation fails and the prompt is blocked Check the backend's login, model, timeout and network. Use /translator input off only if you mean to send the original
The native Claude Code model is unavailable Pin an allowed claudeModel, or set claudeBackend: "pi" and configure Pi
Prompt NOT sent: The loaded translation mod is out of date A mod from before 0.4.0 is still loaded. Restart Claude Code, including any other account that uses the same link
Claude Code prints reload failed, the previous version stays loaded The updated files have an error, or the update was caught halfway. The previous version keeps translating; once the files are complete, Claude Code reloads the mod on their next change, or run /reload-plugins
Translation stopped working after the clone was switched to another branch Agents load Translator straight from the linked clone, so the branch's version, or its absence, is live. Switch it back, and run agents from the released npm package rather than a checkout that switches branches; see Moving or updating your clone
No original under a prompt sent while Claude was working Update to 0.6.2 or later; earlier versions drew the original only under prompts sent while Claude was idle
In the Claude Code desktop app, the Chinese of a prompt sent while Claude works disappears once Claude reads it, and no original appears under the English until the turn ends Update to 0.6.15 or later, then run /reload-plugins in the session or start a new one. Earlier versions drew the original only after Claude Code had written that prompt's row to the session file, which a long step puts off
In the Claude Code desktop app, a prompt shows the Chinese twice, or an answer or a prompt sent while Claude was working has no Chinese under it Update to 0.6.6 or later, then run /reload-plugins in the session or start a new one: the app's sessions do not reload mods by themselves. The app draws rows from its own copy and names them its own way; earlier versions drew only for the terminal's rows. Rows already in a session are drawn again when the app next shows them
In the Claude Code desktop app, a new session has no Input, Instruction and Output row until its first message The app starts Claude Code, and with it the mod, only when a project session's first message is sent. Pick /translator from the / menu, or send a setting such as /translator input off, as the first message: the mod answers it without a model turn and the row appears. See In the desktop app
In the Claude Code desktop app, /translator is missing from a new session's / menu The app lists a new session's commands with every hook off, and earlier versions registered /translator only once a session ran. Update Translator, then open a new session a few minutes later or restart the app: it keeps its list for a while. The command is listed there through the installer's skills/translator link only, not for a copy loaded with --plugin-dir or CLAUDE_CODE_PLUGIN_DIRS
Translations are slow or time out Check /translator status, set the file's timeoutMs to null (no limit, the default) or raise it, use /translator thinking low, or pin a faster translator with /translator model MODEL. A warning that names a part (part 2 of 4) comes from one part of a long answer. In Pi, long answers time out before 0.6.7, which sent each answer in one request: update, then /reload
Claude Code reports Translation incomplete: the reply stopped before its completion marker Both the translator model and its fallback (named in the message) stopped before the end: the output limit, or both models' safeguards, cut them short. Nothing was submitted; split the prompt, pin another model with /translator model MODEL, or switch backends. Earlier versions, without the fallback, reported missing completion marker
The default settings seem to have no effect Branch/session settings take precedence; check /translator status. Use /translator model follow to override a file model pin
Shortcuts do nothing Pi depends on your terminal reporting the key combination. Claude Code's buttons above the prompt have no keys: click them or reach them with Ctrl+X, Tab. Use /translator for settings; cancellation uses the controls below, not a slash command
In Claude Code, a session's Input or Output switched off by itself Before this version, a digit typed into an empty prompt box pressed the matching button after a short pause (1 Input, 2 Instruction, 3 Output), and the change was saved for the session. The buttons no longer have keys; check /translator status and switch the setting back
In Claude Code, Up shows the English of an older prompt Entries that releases before this one saved hold the English. Translator swaps in the original when it knows it (prompts of this session and your recent prompts from the input cache), as you open the entry from an empty box; prompts sent now are saved as typed
In Claude Code, earlier drawings vanished after /resume, /branch or ← for agents Update to this version: it loads the drawings and settings of the session a conversation continues
A mostly Chinese answer is still translated 0.7.0 masks paths, contextual names and existing Chinese sections, and skips a bounded English preface. Substantive English elsewhere still translates, without resending the Chinese body. Use backticks for exact ambiguous strings
A final choice box leaves an answer untranslated in Claude Desktop Use 0.7.0 and /reload-plugins or a new session. Selection-only steps now start translation while the choice waits; native choices and approval controls remain intact
An English answer containing Chinese names or phrases is translated Expected: Chinese in an answer does not stop the translation while English prose remains, and the translator keeps names and paths
An English prompt quoting Chinese is translated Expected: quoted Chinese counts as prose. The translator keeps a quoted literal value; wrap it in backticks to keep the prompt out of translation
A translation leaves prose untranslated or uses an unexpected language There is no post-translation language audit or repair call. Review the result, try another translator model, and disable the input cache to force a fresh translation; wrap exact literal strings in backticks when appropriate
Need to cancel a translation Pi: Ctrl+Alt+C. Claude: the Cancel translation button while a translation runs (Ctrl+X, Tab reaches it; key 4 in the settings pane). Both cancel input/output without changing settings; interrupting the agent stops only a prompt's translation, and do not rely on ordinary Pi abort during idle input preprocessing
File lock timeout / incomplete rollback Stop concurrent settings changes and inspect the reported lock and retained backup; never remove active locks or overwrite newer settings blindly
Claude Code lists "Hooks (0)" for the plugin That count covers classic settings hooks only. Run claude plugin validate to see the mod's events

Limitations

  • Preflight Chinese detection looks for Han characters, so Japanese kanji also count. It runs only before translation. Completed/nonempty responses and literal integrity do not guarantee meaning, completeness of the translation or the requested language; preserved literal content never triggers language rejection.
  • Hidden reasoning, arbitrary tool output, failed/aborted child reports and unfinished answers are not translated. Supported subagent tasks/reports and Claude selection-only steps are covered as described above; operational-tool chatter still skips. Text added afterward by another template/plugin, custom subagent schemas and remote workflows without public local events are outside the contract.
  • Slash commands, skills and prompt templates go out as typed, arguments included: Claude Code never applies a rewrite of a command's line, and Pi splits a template's arguments itself. A line that only starts with a slash, such as a path (/tmp …) or a mistyped command, is translated.
  • In Claude Code, settings UserPromptSubmit hooks see the English. Other mods' prompt.submit hooks beneath Translator, turn.start and a queued prompt's attachment.prompt see the text you typed.
  • Successful originals require delivery evidence. Pi's public API has no input-to-queue ID: exact text and image matching is conservative, and different originals with indistinguishable pending translations, conflicting bypassed inputs, or host-normalized images/text can cause the card to be omitted. Persistent journals and enabled caches remain available. Queue discard, navigation, reload or session replacement never reconstruct pending cards from history; previously delivered cards remain.
  • A classic UserPromptSubmit hook cannot safely replace the original prompt (its additionalContext only adds text), so this tool offers no such fallback and no Codex hook.
  • Claude Code's safe and bare modes, disabled hooks, an untrusted workspace or an organization policy can stop the plugin from loading. With hooks disabled, /translator comes from the mod's SKILL.md alone, and the model replies that Translator is not running; safe and bare modes skip your skills too, so /translator is not available there. A new installation should check /translator status before relying on it.
  • In Claude Code, the originals and Chinese answers drawn in the transcript come back after a restart only from the session journal, so memory-only mode (TRANSLATOR_NO_PERSIST=1) shows them only until Claude Code exits. Their notices remain in the transcript view.
  • A running Pi picks up a code update only at /reload or a restart: Pi lets only commands reload extensions.
  • Claude Code translates an answer after its turn. A reload of the mod during that translation leaves it to the old copy while that copy still runs and otherwise starts it again in the new one, never both; a switch to another session (/clear, /resume, /branch) lets it finish; quitting Claude Code first loses it, as a started record without a result shows. A one-shot claude -p run, which nothing draws, still waits for the translation before it ends. Headless SDK interruption releases the step's wait, not the session-owned output job; the host may nevertheless delay accepting its next prompt until that job finishes. A copy whose session-start runner has not initialized retains the legacy waiting-step path and its dispatch cancellation boundary.
  • A conversation that continues under a new session ID without naming the session it came from (--fork-session, the desktop app's Rewind) is tied to it by the rows it draws: once it draws a row that session linked, the session's drawings and Translator settings come along. Until then, when none of the rows it shows has a drawing, and in a run that nothing draws (claude -p, an SDK host without a surface), it starts from the file defaults.
  • A few rare Claude Code turns finish without a regular model step. Their answers are translated through a de-duplicated fallback, and whether its transcript notice is saved depends on the host; see the Claude Code adapter guide.

Uninstalling

  1. Pi: remove this tool's pi/index.ts entry from extensions in the settings.json of the Pi directory you use, then reload or restart Pi.
  2. Claude Code: remove the skills/translator symlink for each account you set up, then restart Claude Code.
  3. Remove the ~/.local/bin/translator link if you no longer need it.
  4. Remove the ~/.agents/hooks/translator link, or the source itself, only after checking that no other agent still uses it.
  5. If you installed from npm, remove the package with npm uninstall -g @orbit-labs-ai/translator.

Do not delete a whole agent configuration directory, and do not restore an old settings backup over a file that has changed since. These steps leave your configuration, translation history and logs in place; delete them separately using Data and privacy.

Development and testing

To load the tool for a single session without installing it, set HOOK to the translator directory and run pi -e "$HOOK/pi/index.ts" or claude --plugin-dir "$HOOK/claude-code". Do not load the same integration through two entry points at once.

npm run check:shared   # verify Claude's generated scanner matches the shared source
npm test                 # Node tests, plus real Pi and Claude Code against local HTTP fixtures
npm run test:claude      # validate the Claude Code plugin and run its mod tests
npm run test:all         # both of the above; full verification needs both agents installed
npm run test:coverage    # coverage for the Node modules; the scope is documented separately
npm pack --dry-run --json --ignore-scripts  # inspect runtime/license and private-data exclusions
bash -n install.sh

Every model response in the tests comes from a local fixture, so the tests never make paid calls. Installer tests run against temporary home and clone directories and cover all four combinations of installed agents, interactive choices, re-runs, paths with spaces, migration, conflicts, malformed settings, backups and rollback. The real-agent tests load the entries that the installer creates.

When the Pi SDK or Claude Code is missing, the matching integration tests are skipped and say so; a skipped test is not a passed test. Two variables help with debugging:

  • PI_SDK_PATH: path to the Pi SDK's dist/index.js, if Pi is not installed in its usual managed layout.
  • KEEP_TRANSLATION_TEST=1: keep the temporary directories of the Claude Code integration tests.

test/claude-tui.test.mjs and test/pi-tui.test.mjs drive the real Claude Code and Pi terminals through tmux and are skipped, saying so, when tmux or that agent is not installed.

The 0.7.2 verification record covers unmodified official Pi 1.4.0, Claude Code 2.1.311, auxiliary usage and packaging: 1,387 Node tests and 61 native Claude plugin tests passed, zero failures/skips. All checks use isolated local fixtures, not paid providers. The 0.7.1 record retains its earlier compatibility evidence; the 0.7.0 record preserves Pi 1.3.0 / CLI / desktop-engine results and GUI acceptance boundaries. Coverage was not remeasured for 0.7.2.

Further reading

  • Claude Code adapter: how the mod works inside Claude Code.
  • Pi adapter: how the extension works inside Pi.
  • Architecture: source layout, dependency rules and the worker protocol.
  • Testing: test matrix, recorded results and limits.
  • Changelog: what changed in each version, and how to upgrade.
  • Contributing: how to propose changes; the English and Chinese READMEs are maintained together.

License

MIT © 2026 Orbit Labs contributors.