Skip to main content

unwrap-terminal-text

PyPI License: MIT

Fixes copy-paste from Claude Code, Codex CLI, and other terminal AI assistants.

Text copied out of a terminal UI arrives broken. Every line carries the interface's left margin, and long lines contain hard newlines at the wrap column rather than soft wraps — so paragraphs paste as ragged fragments, URLs break in half, and code loses its indentation.

This cleans it up. A command-line filter that works anywhere, plus a GTK4 desktop app for Linux where you pick region by region what gets rejoined.

It is not specific to any one assistant — it works on output from any TUI that hard-wraps, including plain less, journalctl, and SSH sessions. The problem is reported repeatedly against Claude Code (#26016, #48768) and Codex CLI (#12200, #8306). Other assistants that render a left gutter are likely affected the same way, though I have only verified those two.

  ▎ Write down what you'd build before running the command, then check
  ▎ whether the brief changed what you'd build first. If it only changed
  ▎ things you weren't going to act on, the questions aren't earning it.

becomes

Write down what you'd build before running the command, then check whether the brief changed what you'd build first. If it only changed things you weren't going to act on, the questions aren't earning it.

Why a tool instead of a regex

Stripping the margin is easy. Deciding which line breaks were real is not — the terminal destroyed that information on the way to your clipboard. A paragraph wrapped at column 80 and a deliberately short line look identical afterwards.

So the transforms are conservative, and the GUI hands the ambiguous call back to you: select the prose, rejoin it; select the code, leave its line breaks alone.

Install

The CLI is pure standard library — no dependencies, any OS with Python 3.9+:

pipx install unwrap-terminal-text     # or: pip install --user unwrap-terminal-text

or from a clone:

git clone https://github.com/Bishop81/unwrap-terminal-text
cd unwrap-terminal-text
pip install --user .

Desktop app (Linux)

The GUI additionally needs GTK4, libadwaita and PyGObject, which come from your distribution rather than pip:

sudo apt install python3-gi gir1.2-gtk-4.0 gir1.2-adw-1     # Debian/Ubuntu
sudo dnf install python3-gobject gtk4 libadwaita            # Fedora

Then ./install-desktop.sh to register the launcher and icon.

libadwaita targets GNOME, so the GUI is effectively Linux-only. The CLI has no such constraint.

Command line

clipclean notes.txt                 # unwrap a file
clipclean -s code.txt               # keep line breaks, drop the margin
clipclean -b notes.txt              # re-bold lead-in labels

xclip -o | clipclean | xclip -i     # X11 clipboard round trip
wl-paste  | clipclean | wl-copy     # Wayland
pbpaste   | clipclean | pbcopy      # macOS
Flag Effect
-u, --unwrap Drop the margin and rejoin wrapped lines (default)
-s, --strip Drop the margin, keep line breaks — use for code
-b, --bold Re-bold lead-in labels before : or an em dash
--no-markers Keep tool glyphs and blockquote rules
--no-linenos Keep line-number prefixes
--flatten-code Rejoin fenced code blocks too, instead of leaving them alone

Desktop app

Opens with your clipboard already loaded. Operations act on the selected lines, or the whole buffer if nothing is selected.

Key Action
Ctrl+U Unwrap — drop the margin, rejoin wrapped lines
Ctrl+K Strip only — drop the margin, keep line breaks
Ctrl+B Bold leads
Ctrl+Return Copy everything back to the clipboard
Ctrl+R Reload from the clipboard
Ctrl+Z Undo

It also takes a file path, and opens it in the running window.

What it cleans

  • Left margin — removes the common leading indent, so relative indentation inside a code block survives.
  • Hard wraps — rejoins wrapped lines into paragraphs. Blank lines, bullet lists, numbered lists and table rows stay put.
  • Fenced code blocks — anything between ``` or ~~~ fences passes through untouched, so prose around a code block unwraps while the code keeps its own line breaks and indentation. An unclosed fence protects the rest of the text rather than flattening it. --flatten-code opts out.
  • Split URLs — a line ending in a hyphen joins with no gap, so …/claude- + code/issues/1 reassembles correctly.
  • Tool glyphs and blockquote rules⏺ ⎿ ▎ │ and friends at line start.
  • Line-number prefixes1⇥, 1 | , 1→, but only when most lines carry one, so a genuine numbered list is never eaten.
  • ANSI escape codes — always.

Bold leads

Terminals render **bold** as styling and discard the syntax, so it never reaches your clipboard. This puts it back on lead-in labels:

Why: because the terminal ate it.   ->   **Why:** because the terminal ate it.
Line numbers — the 1⇥ prefixes.     ->   **Line numbers** — the 1⇥ prefixes.

It refuses plain hyphens (a well-known thing - not a label), lead-ins over eight words, and anything starting with a quote or bracket, so dict literals, JSON and function signatures are left alone. Running it twice is a no-op.

Known limits

  • Code blocks marked by indentation alone are still flattened by --unwrap; only fenced blocks are detected. In the desktop app, select those and use Strip only. Indent-based detection is planned.
  • A URL broken at a wrap point without a hyphen still gets a space inserted. Recovering that needs the terminal width, which the clipboard doesn't carry.
  • Markdown emphasis other than lead-in labels can't be recovered — the terminal discarded it.
  • Nested blockquote depth is flattened rather than preserved.

Tests

python3 tests/test_core.py    # transforms, no display needed
python3 tests/test_gui.py     # GTK layer, needs a display

samples/ holds fixtures for each transform, including cases that must come back unchanged.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unwrap_terminal_text-0.2.0.tar.gz (16.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

unwrap_terminal_text-0.2.0-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file unwrap_terminal_text-0.2.0.tar.gz.

File metadata

  • Download URL: unwrap_terminal_text-0.2.0.tar.gz
  • Upload date:
  • Size: 16.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for unwrap_terminal_text-0.2.0.tar.gz
Algorithm Hash digest
SHA256 31a91a69401c306781b629fbe04df43403a02e626298d902634ea33ed6acc55e
MD5 678a20bcc626c1efa5b544c35147771f
BLAKE2b-256 1793fd2e38d5c61c898760d60839760c55a1e90baa6dcf8fe7c841e5b7a352f0

See more details on using hashes here.

File details

Details for the file unwrap_terminal_text-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for unwrap_terminal_text-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a2d3a8a02bdd455bb537c47ee96d0f1cbc5e7fd648f426d8ed7aa21fc7cadb94
MD5 51cbe72e13523ba810bb6c5bab461ce8
BLAKE2b-256 1556070d7170ca5372755b24fd72270b08a4e372acae00a4001263e3fd9e4032

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page