Metadata-Version: 2.5
Name: line-review
Version: 0.2.1
Summary: A persistent Git-aware terminal UI for reviewing and flagging individual lines
Author: line-review contributors
License-Expression: MIT
License-File: LICENSE
Keywords: code-review,git,terminal,textual,tui
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Requires-Dist: pathspec<1,>=0.12
Requires-Dist: platformdirs<5,>=4.3
Requires-Dist: pygments<3,>=2.19
Requires-Dist: rich<16,>=14
Requires-Dist: textual<9,>=8.2
Requires-Dist: tree-sitter-markdown<0.6,>=0.5
Requires-Dist: tree-sitter<0.26,>=0.25
Provides-Extra: dev
Requires-Dist: pytest-asyncio<2,>=0.24; extra == 'dev'
Requires-Dist: pytest-cov<8,>=6; extra == 'dev'
Requires-Dist: pytest<9,>=8.3; extra == 'dev'
Requires-Dist: ruff<1,>=0.11; extra == 'dev'
Description-Content-Type: text/markdown

# line-review

`line-review` is a persistent, Git-aware terminal application for reviewing files one
line at a time. It highlights added code and configurable unprofessional language,
lets you classify individual lines as **Problem** or **Re-review**, and exports the
active findings as Markdown and JSON.

The reviewed files are never edited. The only writes inside a target directory are
report files whose destination you explicitly approve.

## Installation

`line-review` requires Python 3.12 or newer. Git must be on `PATH` to use the
Git-aware scopes; ordinary directories can be reviewed without Git. Linters are
optional and are used only if they are already installed.

Install the published package with pip:

```powershell
python -m pip install line-review
line-review C:\path\to\repository
```

Alternatively, run it in an isolated environment with
[uv](https://docs.astral.sh/uv/):

```powershell
uvx line-review C:\path\to\repository
```

The path is optional and defaults to the current directory. A terminal with mouse
support and at least 100 columns is recommended.

## Startup scopes

Every run begins with a scope dialog.

- **This commit** selects only files touched by `HEAD` compared with its first parent.
  It displays the latest working-tree copies of those files, but excludes unrelated
  dirty and untracked paths. A root commit is compared with an empty tree.
- **Full file tree** includes tracked and nonignored untracked text files. In an
  ordinary directory, it recursively scans text files while honoring `.gitignore`
  files and common cache/vendor exclusions.
- **Since a commit** accepts a SHA, tag, branch, or expression such as `HEAD~3`. The
  ref is resolved to a commit and compared from its merge-base with `HEAD` through
  the current working tree, including committed, staged, unstaged, and nonignored
  untracked changes.

When the selected path is a repository subdirectory, review paths and reports remain
relative to that subtree while Git metadata comes from the enclosing repository.
Non-Git directories offer only Full file tree.

## Reviewing

The left sidebar contains four collapsible, independently scrolling lists:

1. Reviewed
2. Unreviewed
3. Problems, with a marked-line count per file
4. Re-review, with a marked-line count per file

The sidebar stays available in narrow terminals in a compact 22-column form; at
under 96 columns the secondary toolbar buttons are hidden but remain available from
their keyboard shortcuts, so **Done** or **Reload file** stays reachable.

Clicking a file opens it. **Ctrl** (or **Cmd**) **+ click** instead flips that file
between Reviewed and Unreviewed without opening it, and **Shift + click** applies the
same change to every row between the last row you clicked and this one. A directory
you have no intention of reading is therefore retired in two clicks: click its first
file, then Shift-click its last. Marking this way stores the same Done baseline as
**Mark done & next**, so those files return to Unreviewed if they later change, and
their Problem and Re-review findings are left alone.

Ctrl- and Shift-click are captured by some terminals before the application sees
them — macOS Terminal.app treats Ctrl-click as a right click. Where that happens,
`Space` on the focused sidebar list does the same thing to the highlighted row.

A file whose contents changed on disk since the scan is skipped rather than
baselined, and the notification says how many were left out.

Reviewed status is content-addressed. **Mark done & next** stores the exact current
snapshot; if the file later changes, it automatically returns to Unreviewed. A file
may be Done and still contain Problem or Re-review findings.

Clicking a source row repeatedly cycles its persistent state:

```text
Unmarked -> Problem -> Re-review -> Unmarked
```

The third state removes the finding completely; no cleared audit entry is retained.
Problem rows use a red `!` marker and Re-review rows use a blue `?` marker.

Press `N` to record why the current line is marked. The note is shown after the
line, is kept when the line is recycled between Problem and Re-review, and is
removed with the finding when the line is cleared. Escape cancels the dialog and
Enter saves; saving an empty note removes it. Only a marked line can carry a
note, because the note is stored on the finding.

- Linter warnings appear in an ALE-style sign column: `>>` for an error, `--` for
  a warning or hint, with the reported span underlined. The full message for the
  line under the cursor is shown beneath the filename. See [Linters](#linters).
- Added and replacement lines since the file's previous Done snapshot have a pale
  green background.
- Built-in and workspace-added keywords have a yellow background. Automatic keyword
  matches are hints only and are not reported until their line is manually marked.
- **New only** retains three context lines around changed chunks and replaces larger
  unchanged ranges with clickable gap rows. Deletion-only hunks receive a compact
  non-markable banner.
- On the first Full file tree review there is no previous baseline, so green
  highlighting and New only are disabled until the file has been marked Done once.
- Markdown is shown as syntax-highlighted source so every visible row retains its
  original line identity.

If a file changes while open, marking and Done are disabled until it is reloaded.
Identical lines are remapped across moves using surrounding context; a finding is
removed when its marked source line has been edited or deleted.

## Mouse and keyboard

The Textual footer also exposes active bindings as clickable controls.

| Input | Action |
| --- | --- |
| Click / `Space` (viewer) | Cycle the current source line |
| `Ctrl`/`Cmd` + click a sidebar file | Mark it Reviewed, or return it to Unreviewed |
| `Shift` + click a sidebar file | Apply that change to the whole range |
| `Space` (sidebar) | Same as Ctrl+click, for the highlighted file |
| `P` | Mark current line Problem |
| `R` | Mark current line Re-review |
| `C` | Clear current line |
| `N` | Add or edit a note on the current marked line |
| `Up` / `Down`, `K` / `J` | Move through displayed rows |
| `Page Up` / `Page Down`, `Home` / `End` | Navigate the viewer |
| `Enter` | Cycle a source row or expand a collapsed gap |
| `D` | Mark file Done and open the next Unreviewed file |
| `F` | Toggle New only |
| `Ctrl+R` | Preview/export a report |
| `Ctrl+S` | Edit workspace keyword additions/exclusions |
| `Ctrl+O` | Choose a new review scope |
| `Q` | Quit |

## Linters

The file being reviewed is passed to whichever supported linters are already on
`PATH`; nothing is installed, configured, or required. Each run happens in a
worker, so warnings appear when they are ready and never delay marking a line.

| Extensions | Linter |
| --- | --- |
| `.py`, `.pyi` | `ruff check` |
| `.sh`, `.bash`, `.ksh`, `.zsh` | `shellcheck` |
| `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx` | `eslint` |
| `.c`, `.cc`, `.cpp`, `.cxx`, `.h`, `.hh`, `.hpp`, `.hxx` | `cppcheck` |

Warnings are hints, exactly like keyword matches: they are never persisted and
never appear in a report until you mark the line yourself. The status area shows
the total for the open file, and the message for the line under the cursor.

Linters see the reviewed snapshot on standard input where they support it, so
the project's own configuration and per-file excludes are honoured. `cppcheck`
cannot read standard input, so it is given the real path and is skipped while a
file is known to have changed on disk. Runs are non-interactive, are given a ten
second limit, and are invoked so that no linter cache is written into the review
tree. A crashing, missing, or slow linter is silently ignored.

Files over 2 MiB are not linted. `--no-lint` turns the whole feature off.

## Keywords

The packaged default list contains common profanity and unprofessional terms such as
`dumb` and `stupid`. Matching is case-insensitive and respects whole word/phrase
boundaries, so a short term does not highlight part of an identifier.

The Settings dialog accepts one addition or exclusion per line. These overrides are
stored only for the current reviewed workspace; they do not create a repository
configuration file.

## History and reports

Review state lives in a per-user SQLite database selected by `platformdirs`—on Windows
this is normally beneath `%APPDATA%\line-review\line-review`. The database stores:

- canonical workspace and keyword settings;
- resumable review sessions and their resolved Git scopes;
- one compressed Done baseline per reviewed file; and
- only currently active Problem/Re-review findings.

Each finding records its file/line/text fingerprint, content hash, note, timestamp,
full `HEAD` SHA, every exact tag pointing at `HEAD`, branch/describe metadata, and
whether the working tree was dirty. Git metadata is read when the review is scanned
and refreshed whenever a file is marked Done or a report is exported, so marking a
line never waits for Git. Non-Git reviews leave Git fields empty.

When all eligible files in the current scope are Done, the report dialog opens
automatically. It defaults to current-scope findings and offers an **all active
workspace findings** toggle. Export writes sibling `.md` and `.json` files from the
chosen basename. Existing targets require confirmation. The pair is protected by a
single-process export lock and rollback: both complete temporary files are prepared
before replacement, and a failed replacement restores the prior pair. Reports written
beneath the review root are remembered and excluded from later scans.

JSON includes `schema_version: 2`; both formats share a unique report ID, group
findings into Problem and Re-review sections, carry each finding's note, and include
review coverage, summary scope, Git scope, tag, dirty, reviewed, and skipped-file
metadata. The report preview is revalidated when it is submitted, so a file that
changes while the completion dialog is open returns to Unreviewed instead of
completing stale work.

Binary files, submodules, inaccessible files, deleted paths, and symlinks escaping the
selected root are skipped and do not block completion. Text files over 2 MiB or 50,000
lines remain reviewable but use plain-text rendering to keep navigation responsive.
For an in-root symlink, Git historical comparison is disabled until the displayed
target has its own Done snapshot, avoiding a comparison between link text and target
contents.

## Development

```powershell
uv sync --extra dev
uv run ruff check src tests
uv run pytest
```

The tests create temporary Git repositories and use Textual's headless Pilot to verify
mouse hit mapping, click cycling, syntax/keyword layers, collapsed hunks, sidebar
counts, persistence, Done/next navigation, and report prompting/export.
