gvzdv/claudish-to-english: Contribute to gvzdv/claudish-to-english development by creating an account on GitHub.

github.com · 2026-08-21 · by Long Nguyen ·github
Contribute to gvzdv/claudish-to-english development by creating an account on GitHub.

gvzdv/claudish-to-english

Contribute to gvzdv/claudish-to-english development by creating an account on GitHub.

1.9k stars · 96 forks

README

claudish-to-english

A Claude Code plugin that shows a plain-English rewrite of each assistant message, produced by a local LLM via ollama (default), the codex CLI, the Anthropic API, or any OpenAI-compatible API. It is display-only: Claude's own reasoning and the saved transcript keep the original text — only what you read on screen changes. If your session speaks something other than English, so does the rewrite: it follows the message's own language, or the language setting Claude Code already answers in. See Output language. An optional second hook rewrites Markdown files into plain language when they are written or edited (opt-in, off by default).

Status: working prototype. Every hook fails open — if anything goes wrong (provider down, timeout, missing key or dependency), you simply see Claude's original text. The plugin can never swallow or corrupt an answer.

Requirements (read this first) With the default ollama provider this plugin shells out to a local model, and nothing works until these are in place. (With CLAUDISH_PROVIDER=anthropic or openai you need only jq, curl, and an API key — see Providers.)

macOS setup

Requirement Why Install

ollama, running Does the rewriting, locally brew install ollama then ollama serve

A pulled model The actual rewriter ollama pull gemma4:26b-mlx (~17 GB; choose the model that fits into your memory)

jq Parses hook JSON ships with macOS 15+; else brew install jq

curl Talks to ollama ships with macOS

Warm the model once after ollama serve (the first call is a slow cold load): ollama run gemma4:26b-mlx "hi" If the local model isn't ready, the plugin does nothing to your text — Claude's output shows normally, unchanged. That is by design, not a bug. It skips (fails open) when ollama is down, the request times out, or the model isn't pulled. The first time that happens in a session it tells you why: the display hook appends a one-line notice on screen, and the Markdown hook shows a systemMessage. So a silent skip is never a mystery (once per session; set CLAUDISH_NOTICE=0 to silence it). Pick a model you actually have. The default is gemma4:26b-mlx, an Apple-silicon (MLX) build — the right choice on a Mac, but macOS-only. On Windows it doesn't run, so you must switch to a regular tag (see Windows setup). Pull it (as above), or pull a smaller/faster model and point the plugin at it by setting CLAUDISH_MODEL to that model's exact ollama tag in your env (see Configuring the plugin). If CLAUDISH_MODEL names a model you have not pulled, every rewrite is skipped — with the one-time notice above.

Windows setup The hooks are bash scripts; on Windows, Claude Code runs them through Git Bash (Git for Windows).

Requirement Why Install

Ollama, running Does the rewriting, locally winget install Ollama.Ollama, then launch the Ollama app; it serves on localhost:11434

A pulled model The actual rewriter ollama pull gemma4:26b (choose a model that fits into your memory)

jq Parses hook JSON winget install jqlang.jq

curl Talks to ollama ships with Windows 10+

Git Bash Runs the hook scripts Claude Code users usually already have it; else winget install Git.Git

Restart your terminal after installing so jq, ollama, and Git Bash are on PATH (check jq --version and ollama --version).

The default model is macOS-only — Windows users must override it. The plugin's default, gemma4:26b-mlx, is an Apple-silicon (MLX) build that doesn't run on Windows, so leaving it unset means every rewrite is silently skipped. Always set CLAUDISH_MODEL to a regular (non-MLX) tag on Windows. The table above uses gemma4:26b as an example; choose another if it fits your machine better.

Warm the model once after launching Ollama (the first call is a slow cold load): ollama run gemma4:26b "hi" Then set CLAUDISH_MODEL in the env block of your settings.json (see Configuring the plugin — that method is identical on Windows), or for a one-off session from PowerShell: $env:CLAUDISH_MODEL = "gemma4:26b"; claude Windows equivalents of the mid-session kill switch (Toggling mid-session): New-Item -ItemType File $HOME\.claude\claudish-off # pause rewrites Remove-Item $HOME\.claude\claudish-off # resume (In Git Bash the touch/rm commands from that section work as-is.) Notes:

Write CLAUDISH_MD_DIR with forward slashes (C:/dev/docs/plain) so the bash-side path checks match The CLAUDISH_DEBUG=1 log lands under Git Bash's temp directory ($TMPDIR/claudish-to-english/, typically C:\Users\<you>\AppData\Local\Temp\claudish-to-english\).

Install Directly from this repository (also serves its own marketplace): /plugin marketplace add gvzdv/claudish-to-english /plugin install claudish-to-english@gvzdv-plugins After review by the Anthropic team, the plugin will be available to install from the community marketplace: /plugin marketplace add anthropics/claude-plugins-community /plugin install claudish-to-english@claude-community If the install summary says Run /reload-plugins to activate., run that command. Try before installing (loads it for one session, no install): claude --plugin-dir /path/to/claudish-to-english Run /reload-plugins after edits; if it doesn't load, check the /plugin Errors tab.

Configuring the plugin There are two ways to control claudish, and they complement each other:

/claudish — a slash command for live, day-to-day toggles: on/off, display mode, language, and model. Changes take effect immediately, mid-session. This is what most people reach for. See Controlling it live. CLAUDISH_* environment variables — your durable defaults, and the only way to set everything /claudish doesn't cover (provider, API keys, base URLs, the Markdown hook, timeouts). Full list in Configuration below.

Set the durable ones in Claude Code's env block in settings.json — do not edit the plugin's own hooks/hooks.json, which lives in the read-only plugin cache (~/.claude/plugins/cache/…) and is overwritten on every update. For a personal, all-projects setup, use ~/.claude/settings.json: { "env": { "CLAUDISH_MODEL": "gemma4:26b-mlx", "CLAUDISH_MODE": "append" } } The hooks are subprocesses Claude Code spawns, so they inherit these. A few things to know:

Restart Claude Code after editing env. The value is captured at launch, so a running session keeps the old one. env does not merge across scopes. The highest-precedence settings file that defines env supplies the entire block — it isn't combined with lower scopes. Precedence: managed → local → project → user. Keep all your CLAUDISH_* vars in whichever file wins. Scopes: ~/.claude/settings.json (all your projects) · .claude/settings.json (shared with a repo, checked in) · .claude/settings.local.json (just you, just this repo).

Quick one-off without editing a file — hooks inherit the launching shell: CLAUDISH_MODEL=llama3.2:3b claude To confirm the hook is firing, set CLAUDISH_DEBUG=1 and watch "$TMPDIR"/claudish-to-english/debug.log.

Controlling it live: /claudish /claudish is the interactive front-end for the four most-used settings — on/off, display mode, language, and model. Changes take effect on the next assistant message, mid-session, with nothing to relaunch: /claudish show the dashboard (current state + where each value comes from) /claudish on resume rewrites (keeps the current mode) /claudish off pause rewrites (originals only; also pauses the Markdown hook) /claudish append show the original, then the rewrite below it /claudish replace show only the rewrite /claudish style tldr rewrite as a short summary (or "5y" = explain like I'm five) /claudish style reset to the default plain-language rewrite /claudish language fr rewrite into French (any name, incl. non-Latin like 简体中文) /claudish language reset to the session/settings language (see "Output language") /claudish model X use model X for whatever provider is configured /claudish model reset to the provider default /claudish last reprint the ORIGINAL of the last message (handy in replace mode) /claudish cycle step off → append → replace → off /claudish reset clear ALL overrides — back to your env/settings defaults

Bare /claudish prints a dashboard — each setting, its current value, and where that value comes from (an env var, a /claudish override, your settings.json, or the default): claudish · plain-language rewrite of each assistant message

status replace · ⚠ /claudish — rewrite only; beats env, persists across sessions style tldr · ⚠ /claudish — beats env CLAUDISH_STYLE, persists across sessions language French · ⚠ /claudish — beats env & settings, persists across sessions model gemma4:26b-mlx · ollama provider default provider ollama · default

Each switch writes a small flag file under ~/.claude/ that the hooks re-read every message (the paths and their CLAUDISH_*_FILE overrides are in the env-var table; the underlying mechanism is under Toggling mid-session). A live /claudish switch beats the matching env var — CLAUDISH_MODE, CLAUDISH_LANG, or CLAUDISH_MODEL. These overrides persist across sessions (like the off-file), until you clear them with the per-setting default form, /claudish on, or /claudish reset. So a /claudish language French set today still applies to a session you open tomorrow. The state is never silent, though: the dashboard flags every persisting override with ⚠, and a SessionStart notice lists any that are active at the top of a new session (CLAUDISH_NOTICE=0 silences it). For a permanent default, set the matching env var in settings.json rather than using /claudish. /claudish last works because the rewrite is display-only: the transcript always keeps Claude's original text, so the command just reprints it (prefixed with an internal <!-- claudish:original --> marker that tells the display hook not to rewrite that reply, and which is stripped from view).

Output language The rewrite comes back in the same language as the text it rewrites. An Esperanto answer is simplified into Esperanto, an English one into English; nothing is translated unless you ask for it. To pin a language, set one. The hooks read the same language key Claude Code itself answers in, in the same order of precedence, so a session that already speaks Esperanto needs no extra configuration:

Source Example

CLAUDISH_LANG (env) "CLAUDISH_LANG": "Esperanto"

<project>/.claude/settings.local.json "language": "Esperanto"

<project>/.claude/settings.json "language": "Esperanto"

~/.claude/settings.json "language": "Esperanto"

The first one that is set wins, and the on-screen label then names it: 💬 In plain Esperanto:. CLAUDISH_LANG set but empty ignores the settings key and goes back to following the input's language; set it to English to force English on a non-English session. A configured language is appended to the built-in prompt. A prompt supplied through CLAUDISH_PROMPT_FILE or CLAUDISH_MD_PROMPT_FILE replaces the whole prompt, language line included — that file is your prompt in full, and it states its own language (see below). An unreadable, malformed, or non-string setting is ignored the way every other failure here is: rewrites keep working, in the input's language. One caveat: a rewrite is only as good as the model that writes it, and small local models simplify English noticeably better than they simplify anything else. If non-English results disappoint, try a larger model or a cloud provider (see Providers).

Customizing the rewrite prompt Each hook ships with a default system prompt that asks the model for plain language while preserving facts, code, and structure. For a quick change of tone without writing a prompt, the display hook also has two built-in style presets — tldr (a short summary) and 5y (explain like I'm five) — set with /claudish style or CLAUDISH_STYLE. For full control, you can instead replace either prompt with your own to add specific rules or use wording that works better with your model (this wins over a style preset). To do so, point the hook at a file that holds the prompt:

Hook Prompt file

Display (rewrite.sh) CLAUDISH_PROMPT_FILE

Markdown (rewrite-md.sh) CLAUDISH_MD_PROMPT_FILE

The file's contents replace the built-in prompt, so include every instruction you want the model to follow — otherwise the defaults (keep facts, leave code blocks alone, output only the rewrite) are gone. Keeping the prompt in a file avoids escaping a long, multi-line prompt inside a JSON string. If the variable is unset, or the file is empty or unreadable, the hook falls back to its built-in default, so a bad path never stops rewrites. { "env": { "CLAUDISH_PROMPT_FILE": "/ABS/PATH/prompts/plain.txt", "CLAUDISH_MD_PROMPT_FILE": "/ABS/PATH/prompts/md-plain.txt" } } The display hook still appends the original user question to your prompt, as context to keep the rewrite on-topic (see How the display hook works). Your prompt replaces only the base instruction.

How the display hook works Claude Code fires the MessageDisplay event once per streamed chunk, not once per message. Each fire is a separate process carrying message_id, index, a final flag, and this chunk's delta (a text fragment, not the whole message). So the hook buffers every delta to a temp file (keyed by message_id) and only calls the model on the final chunk, once the whole message is known: chunk 0 (final:false) ─┐ chunk 1 (final:false) ─┤ append each delta to $TMPDIR/claudish-to-english/<session>/<message>/<index>.part chunk 2 (final:false) ─┘ → emit nothing (append) or "" (replace) chunk 3 (final:true) ──► reconstruct full message → call ollama once → show the rewrite → delete the buffer

On that final chunk it also reads the original user question from the transcript and passes it to the model as context only — to keep the rewrite on-topic. The model is told never to answer or repeat the question; it only rewrites the assistant's message. Display modes

CLAUDISH_MODE On screen Notes

append (default) Original streams normally, then a 💬 In plain language: block is appended (💬 In plain Esperanto: when a language is configured). Safest. No streaming loss; if the LLM fails you just don't get the extra block.

replace Only the simplified version (original chunks suppressed while streaming). Experimental. Appears all at once after LLM latency; on failure it re-shows the full original.

Markdown file rewrite (optional second hook) A PostToolUse hook (rewrite-md.sh) rewrites Markdown files into plain language when they are written or edited. Unlike the display hook, this changes bytes on disk. Opt-in by directory. It does nothing unless CLAUDISH_MD_DIR is set, and it only touches *.md files whose resolved path is inside that directory. Every other README, CLAUDE.md, or doc you edit is left alone.

CLAUDISH_MD_MODE Result Notes

sibling (default) Writes NAME.plain.md next to NAME.md. Non-destructive; the original is never touched.

overwrite Replaces NAME.md in place. Adds a <!-- claudish-to-english:rewritten --> marker so a re-write is skipped (idempotent). A weak model can degrade real docs — use with care.

In both modes: YAML frontmatter is split off and re-attached verbatim, fenced code is left to the model instruction, short files are skipped, and the write is atomic. Fail-open here means the file is left exactly as the agent wrote it. Large files are slow. gemma4:26b-mlx (the default) rewrites at roughly 60 tokens/s, so a long plan or spec can take 30–120s. This hook allows up to CLAUDISH_MD_TIMEOUT (150s) inside a 180s PostToolUse hook budget; if a rewrite still times out you get the one-time notice above — raise those limits, or set CLAUDISH_MODEL to a smaller model. Enable it for one directory, in sibling mode (the safe default), the same way as every other setting — the env block of your settings.json: { "env": { "CLAUDISH_MD_DIR": "/ABS/PATH/docs/plain", "CLAUDISH_MD_MODE": "sibling" } } In overwrite mode the marker comment is written after any YAML frontmatter, so the frontmatter stays on line 1 where parsers expect it.

Providers Rewrites go through one of four providers, selected with CLAUDISH_PROVIDER (both hooks share the setting). The default is unchanged from upstream: local ollama, nothing leaves your machine.

Provider Endpoint Key Default model

ollama (default) CLAUDISH_OLLAMA (http://localhost:11434) none gemma4:26b-mlx

codex OpenAI codex CLI (codex exec) — uses the CLI's own login none (CLI default)

anthropic CLAUDISH_ANTHROPIC_URL (https://api.anthropic.com) + /v1/messages CLAUDISH_ANTHROPIC_KEY or ANTHROPIC_API_KEY claude-haiku-4-5

openai CLAUDISH_OPENAI_URL + /chat/completions CLAUDISH_OPENAI_KEY or OPENAI_API_KEY gpt-5.6-luna

codex CLAUDISH_PROVIDER=codex runs the rewrite through the OpenAI codex CLI (codex exec, read-only sandbox, outside any repo), using the CLI's own login. No API key and no local model server. CLAUDISH_MODEL overrides the CLI's configured model; unset uses the CLI default. CLAUDISH_CODEX_EFFORT overrides the CLI's reasoning effort for the rewrite only (e.g. low keeps a per-message rewrite fast even when the CLI's coding default is a high-effort tier). Requires codex on PATH; fails open like every other provider. CautionThe cloud providers pick their key up from the ambient environment (OPENAI_API_KEY / ANTHROPIC_API_KEY), and CLAUDISH_OPENAI_URL defaults to api.openai.com. Anything that puts those variables into the environment Claude Code launches with — an export in your shell profile, a tool like direnv loading a project's .env into your shell, or the env block of a settings file — makes them visible to this plugin. In such an environment, setting the single variable CLAUDISH_PROVIDER=openai starts sending every assistant message (and, with the Markdown hook, file contents) to OpenAI's cloud. Likewise, CLAUDISH_PROVIDER=anthropic will quietly spend the same ANTHROPIC_API_KEY (and share its rate limits) that other tools on your machine may rely on. Selecting a cloud provider IS the consent switch — set it only when you mean it, and use the CLAUDISH_*_KEY variables when you want the plugin on a dedicated key.

ollama (default) — local, nothing leaves your machine

export CLAUDISH_PROVIDER=ollama export CLAUDISH_MODEL=gemma4:26b-mlx # the default; any pulled tag works

Anthropic — Claude Haiku

export CLAUDISH_PROVIDER=anthropic export ANTHROPIC_API_KEY=sk-ant-... export CLAUDISH_MODEL=claude-haiku-4-5 # the default; override to taste

OpenAI — GPT-5.6 Luna

export CLAUDISH_PROVIDER=openai export OPENAI_API_KEY=sk-... export CLAUDISH_MODEL=gpt-5.6-luna # the default; override to taste

Any OpenAI-compatible server (LM Studio, llama.cpp server, vLLM, OpenRouter).

A key is only required for api.openai.com — local servers work keyless.

export CLAUDISH_PROVIDER=openai export CLAUDISH_OPENAI_URL=http://localhost:1234/v1 export CLAUDISH_MODEL=qwen3-30b Notes:

CLAUDISH_MODEL overrides any provider's default model. Requests to api.openai.com send reasoning_effort: "none" (GPT-5.6-class models otherwise spend reasoning tokens on a plain rewrite). Custom OpenAI-compatible URLs get no such field, since some local servers reject unknown fields. Force one with CLAUDISH_OPENAI_EFFORT, or set it explicitly empty (CLAUDISH_OPENAI_EFFORT=) to omit the field even for api.openai.com — needed for models that reject reasoning_effort entirely. The anthropic provider caps completions at CLAUDISH_MAX_TOKENS (default 4096, since the Messages API requires an explicit cap). A rewrite that hits an output-token cap is discarded, not shown — on the ollama, anthropic, and openai providers (ollama's done_reason: "length" included): a half-finished rewrite on screen is confusing, and in the Markdown hook's overwrite mode it would replace your real document. You get the original text plus the once-per-se

source

github.com · 1 images · 97 links

Comments (0)

No comments yet