Metadata-Version: 2.4
Name: unwrap-terminal-text
Version: 0.1.0
Summary: Clean text copied out of a terminal UI: drop the left margin, rejoin hard-wrapped lines
Author: Chris Morris
License: MIT
Project-URL: Homepage, https://github.com/Bishop81/unwrap-terminal-text
Project-URL: Issues, https://github.com/Bishop81/unwrap-terminal-text/issues
Keywords: clipboard,terminal,text,unwrap,word-wrap,cli,claude-code,codex-cli,ai-assistant,copy-paste,tui
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Text Processing :: Filters
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# unwrap-terminal-text

**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](https://github.com/anthropics/claude-code/issues/13378)
([#26016](https://github.com/anthropics/claude-code/issues/26016),
[#48768](https://github.com/anthropics/claude-code/issues/48768)) and
[Codex CLI](https://github.com/openai/codex/issues/24685)
([#12200](https://github.com/openai/codex/issues/12200),
[#8306](https://github.com/openai/codex/issues/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+:

```sh
pipx install git+https://github.com/Bishop81/unwrap-terminal-text
```

or from a clone:

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

Not on PyPI yet, so install from the repository for now.

### Desktop app (Linux)

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

```sh
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

```sh
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 |

## 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.
- **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 prefixes** — `1⇥`, `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

- 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

```sh
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
