rtl-tty
Fix Persian / Arabic / Hebrew text in terminal AI tools (Claude Code and friends).
Mixed right-to-left and English text in a terminal often comes out scrambled: words swap places,
English terms jump to the wrong side of the sentence. rtl-tty runs your command inside a
pseudo-terminal and tells the terminal which rows are right-to-left, so they are laid out
right-to-left with English words staying where they belong.
rtl-tty claude
Before / after
Real screenshots of Claude Code in GNOME Terminal (the two replies are different texts; both are mixed Persian and English).
Without rtl-tty: rows are laid out left-to-right, so words and English terms end up in the wrong order.
With rtl-tty claude: rows are right-to-left, English terms stay where they belong.
Install
pipx install git+https://github.com/komeilsedaghat-dev/rtl-tty
(pipx install rtl-tty will work once the package is published on PyPI.)
Requires Python 3.9+ on Linux. No dependencies. Optional alias:
alias claude='rtl-tty claude'
Compatibility
| Terminal / tool | Status |
|---|---|
| GNOME Terminal 3.52 (VTE 0.76), Claude Code 2.1 | works (tested) |
| Other VTE terminals (Tilix, Terminator, ...) | expected to work, untested |
| tmux / screen | untested; likely to swallow the escape sequence |
| Kitty, Alacritty, WezTerm, iTerm2, Windows Terminal, VS Code terminal | not supported (no bidi layout at all in most of them) |
| Gemini CLI, Codex CLI, aider, opencode | untested |
Reports welcome: run bash experiments/scp_demo.sh in your terminal and open a
compatibility report.
How it works
Findings on GNOME Terminal / VTE 0.76, all reproducible with experiments/scp_demo.sh
(right-aligned rows in the screenshot below are the ones where SCP took effect):
- VTE lays rows out left-to-right by default and discards Unicode bidi control characters (RLI, PDI, RLM, ...), so the usual "wrap it in isolates" trick does nothing (demo case 5).
- VTE has its own escape for this, SCP (
CSI 2 SPACE k= this row is right-to-left). It works on a fresh row even with the cursor indented (demo cases 2 and 4), and the setting persists on the following rows until reset withCSI 0 SPACE k. - On a row that already has text, SCP only takes effect when sent in column 0 (demo cases 6 and 7: the same rewrite is scrambled with SCP in column 1 and right-to-left with SCP in column 0). Claude Code redraws existing rows every frame and indents them, so a plain SCP has no effect there.
rtl-tty therefore buffers each output row until the cursor leaves it, decides once whether the
row contains RTL letters, and inserts ESC 7, CR, SCP, ESC 8 (save cursor, go to column 0,
set direction, restore cursor) before the row's first text. Nothing else in the stream is changed.
Options
rtl-tty [--mode scp|off] [--log FILE] COMMAND [ARGS...]
--mode off passes output through unchanged (handy for before/after comparisons).
--log FILE (or RTL_TTY_LOG) records raw and transformed output for debugging. The log
contains everything shown on screen.
Limitations
- Direction is decided per row; a very long paragraph that the app wraps itself is handled row by row.
- Only rows containing RTL letters change; box-drawing and English rows are left alone.
- Window resizing and scrollback were not tested thoroughly.
Development
python3 -m pytest
License
MIT
فارسی
rtl-tty متن فارسی (و عربی و عبری) را در ابزارهای هوش مصنوعی داخل ترمینال درست نمایش میدهد؛
مثلاً وقتی با Claude Code فارسی و انگلیسی را قاطی مینویسید و کلمهها جابهجا میشوند.
rtl-tty claude
روش کار: ترمینال VTE (مثل GNOME Terminal) دستور مخصوص خودش را برای «این سطر راستبهچپ است» دارد (SCP) و کاراکترهای نامرئی جهتدهنده Unicode را نادیده میگیرد. این برنامه قبل از هر سطر فارسی همان دستور را میفرستد. فعلاً فقط روی GNOME Terminal با Claude Code تست شده است.
Release files for rtl-tty 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rtl_tty-0.2.0.tar.gz | 9.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rtl_tty-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.2 kB
Release files / rtl_tty-0.2.0.tar.gz
| Download URL | rtl_tty-0.2.0.tar.gz |
|---|---|
| Size | 9.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cf940eb92a4f7c4192964caaf6c2d9b2b94cdf9c94e794b1b9ffe9c075870a83
|
|
BLAKE2b-256 checksum How to use checksums |
68e6e4728cb11604bb7038a34d2598c79665d8eb419fabf8fc42814489c007a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency logRelease files / rtl_tty-0.2.0-py3-none-any.whl
| Download URL | rtl_tty-0.2.0-py3-none-any.whl |
|---|---|
| Size | 8.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ff5f98f3ddee0b6237adcea5d0a2b3bf8e3b2f894d8c551eb41a1c0f530e9f43
|
|
BLAKE2b-256 checksum How to use checksums |
8e1e979b2d009e618a4d768405329d4344baae42454e0d9b027c02686bbf96f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.
Transparency log