Agent integration guide
Quick Start
Section titled “Quick Start”🤖 Agent-Friendly CLI (New!)
Section titled “🤖 Agent-Friendly CLI (New!)”For maximum simplicity, you can now invoke redlines without specifying a command. It automatically outputs JSON (the most agent-friendly format):
# Simplest invocation - just provide two strings/filesredlines "source text" "test text"
# Pretty-print for readabilityredlines --pretty "source text" "test text"
# Works with files tooredlines old_version.txt new_version.txtWhy this is better for agents:
- No need to choose between
text,json,markdown,statscommands upfront - JSON output is structured and parseable
- Consistent, predictable behavior
- Fewer tokens needed in prompts
Traditional commands still work if you need specific output formats (see Output Formats).
Installation
Section titled “Installation”# Basic installationpip install redlines
# With PDF file comparison supportpip install redlines[pdf]
# With advanced sentence tokenization (Python 3.11+)pip install redlines[nupunkt]
# With Levenshtein distance metricspip install redlines[levenshtein]Your First Comparison (30 seconds)
Section titled “Your First Comparison (30 seconds)”from redlines import Redlines
# Compare two stringsdiff = Redlines( "The quick brown fox jumps over the lazy dog.", "The quick brown fox walks past the lazy dog.")
# Get markdown outputprint(diff.output_markdown)# Output: The quick brown fox <del>jumps over </del><ins>walks past </ins>the lazy dog.CLI Quick Start
Section titled “CLI Quick Start”# Compare strings (command-less, outputs JSON by default)redlines "Hello world" "Hi world"
# Pretty-print JSON outputredlines --pretty "Hello world" "Hi world"
# Compare files (auto-detected, command-less)redlines old_version.txt new_version.txt
# Or use explicit commands for specific output formatsredlines text "Hello world" "Hi world"redlines json old_version.txt new_version.txt --prettyredlines markdown file1.txt file2.txt --markdown-style ghfm
# Check if files differ (for CI/CD)if redlines file1.txt file2.txt > /dev/null 2>&1; then echo "Files have changes"else echo "Files are identical"fiCommon Patterns
Section titled “Common Patterns”Pattern 1: Compare Two Files
Section titled “Pattern 1: Compare Two Files”from pathlib import Pathfrom redlines import Redlines
# Read filessource = Path("old_version.txt").read_text()test = Path("new_version.txt").read_text()
# Comparediff = Redlines(source, test)
# Get resultsprint(f"Total changes: {diff.stats().total_changes}")print(diff.output_markdown)Pattern 1b: Compare PDF Files
Section titled “Pattern 1b: Compare PDF Files”from redlines import Redlinesfrom redlines.pdf import PDFFile
# Load PDF files (requires: pip install redlines[pdf])source = PDFFile("contract_v1.pdf")test = PDFFile("contract_v2.pdf")
# Comparediff = Redlines(source, test)
# Get resultsprint(f"Total changes: {diff.stats().total_changes}")print(diff.output_markdown)
# Access page informationprint(f"Source has {source.page_count} pages")for page in source.pages: print(f"Page {page.page_number}: {len(page.text)} chars")# CLI: PDF files are auto-detectedredlines contract_v1.pdf contract_v2.pdf --prettyPattern 2: Get Machine-Readable JSON
Section titled “Pattern 2: Get Machine-Readable JSON”import jsonfrom redlines import Redlines
diff = Redlines(source, test)
# Get JSON outputjson_output = diff.output_json(pretty=True)data = json.loads(json_output)
# Process changesfor change in data["changes"]: print(f"{change['type']}: {change.get('source_text', '')} → {change.get('test_text', '')}")Pattern 3: Filter Specific Operations
Section titled “Pattern 3: Filter Specific Operations”from redlines import Redlines
diff = Redlines(source, test)
# Get only insertionsinsertions = diff.get_changes(operation="insert")for change in insertions: print(f"Added: {change.test_text}")
# Get only deletionsdeletions = diff.get_changes(operation="delete")for change in deletions: print(f"Removed: {change.source_text}")
# Get only replacementsreplacements = diff.get_changes(operation="replace")for change in replacements: print(f"Changed: {change.source_text} → {change.test_text}")Pattern 4: Collect Statistics
Section titled “Pattern 4: Collect Statistics”from redlines import Redlines
diff = Redlines(source, test)stats = diff.stats()
print(f"Total changes: {stats.total_changes}")print(f"Insertions: {stats.insertions}")print(f"Deletions: {stats.deletions}")print(f"Replacements: {stats.replacements}")print(f"Change ratio: {stats.change_ratio:.1%}")print(f"Characters added: {stats.chars_added}")print(f"Characters deleted: {stats.chars_deleted}")print(f"Net change: {stats.chars_net_change}")Pattern 5: Batch Process Multiple Files
Section titled “Pattern 5: Batch Process Multiple Files”from pathlib import Pathfrom redlines import Redlines
def compare_directory(dir1: Path, dir2: Path): """Compare all matching files in two directories.""" results = []
for file1 in dir1.glob("*.txt"): file2 = dir2 / file1.name if not file2.exists(): continue
diff = Redlines( file1.read_text(), file2.read_text() )
stats = diff.stats() if stats.total_changes > 0: results.append({ "file": file1.name, "changes": stats.total_changes, "change_ratio": stats.change_ratio })
return results
# Usageresults = compare_directory(Path("old/"), Path("new/"))for result in results: print(f"{result['file']}: {result['changes']} changes ({result['change_ratio']:.1%})")Pattern 6: Generate HTML Report
Section titled “Pattern 6: Generate HTML Report”from redlines import Redlines
diff = Redlines(source, test, markdown_style="none")
html_template = f"""<!DOCTYPE html><html><head> <style> del {{ color: red; text-decoration: line-through; }} ins {{ color: green; text-decoration: underline; }} </style></head><body> <h1>Diff Report</h1> <div>{diff.output_markdown}</div></body></html>"""
Path("report.html").write_text(html_template)Output Formats
Section titled “Output Formats”Markdown Styles
Section titled “Markdown Styles”from redlines import Redlinesfrom redlines.enums import MarkdownStyle
# Available styles:styles = { "red_green": MarkdownStyle.RED_GREEN, # Red strikethrough + green bold (default) "none": MarkdownStyle.NONE, # Plain <del>/<ins> HTML tags "red": MarkdownStyle.RED, # All changes in red "ghfm": MarkdownStyle.GHFM, # GitHub Flavored Markdown "bbcode": MarkdownStyle.BBCODE, # BBCode format "streamlit": MarkdownStyle.STREAMLIT, # Streamlit-compatible}
# Use a stylediff = Redlines(source, test, markdown_style=MarkdownStyle.GHFM)print(diff.output_markdown)Rich Terminal Output
Section titled “Rich Terminal Output”from redlines import Redlinesfrom rich import print as rprint
diff = Redlines(source, test)
# Get Rich-formatted output for terminalrprint(diff.output_rich)JSON Output
Section titled “JSON Output”import jsonfrom redlines import Redlines
diff = Redlines(source, test)
# Pretty-printed JSONjson_str = diff.output_json(pretty=True)
# Compact JSONjson_str = diff.output_json(pretty=False)
# Parse and usedata = json.loads(json_str)JSON Schema Reference
Section titled “JSON Schema Reference”Complete JSON Structure
Section titled “Complete JSON Structure”{ "source": "original text", "test": "modified text", "source_tokens": ["token1", "token2", " ¶ "], "test_tokens": ["token1", "token3", " ¶ "], "changes": [ { "type": "replace", "source_text": "token2", "test_text": "token3", "source_position": [1, 2], "test_position": [1, 2], "source_char_position": [7, 13], "test_char_position": [7, 13] } ], "stats": { "total_changes": 1, "deletions": 0, "insertions": 0, "replacements": 1, "longest_change_length": 6, "shortest_change_length": 6, "average_change_length": 6.0, "change_ratio": 0.15, "chars_added": 6, "chars_deleted": 6, "chars_net_change": 0, "levenshtein_distance": 3 }}Field Descriptions
Section titled “Field Descriptions”Root Fields
Section titled “Root Fields”source(string): Original texttest(string): Modified textsource_tokens(array of strings): Tokenized source text (¶marks paragraph boundaries)test_tokens(array of strings): Tokenized test text
Change Object
Section titled “Change Object”type(string): Operation type -"insert","delete", or"replace"source_text(string | null): Original text (null for insertions)test_text(string | null): Modified text (null for deletions)source_position(array of 2 ints | null): Token position[start, end]in source (null for insertions)test_position(array of 2 ints | null): Token position[start, end]in test (null for deletions)source_char_position(array of 2 ints | null): Character position[start, end]in sourcetest_char_position(array of 2 ints | null): Character position[start, end]in test
Stats Object
Section titled “Stats Object”total_changes(int): Total number of change operationsdeletions(int): Count of deletion operationsinsertions(int): Count of insertion operationsreplacements(int): Count of replacement operationslongest_change_length(int): Length of longest change in charactersshortest_change_length(int): Length of shortest change in charactersaverage_change_length(float): Mean change length in characterschange_ratio(float): Percentage of text modified (0.0 to 1.0)chars_added(int): Total characters added (insertions + replacement additions)chars_deleted(int): Total characters deleted (deletions + replacement deletions)chars_net_change(int): Net character change (added - deleted)levenshtein_distance(int | null): Edit distance (null if Levenshtein library not installed)
Decision Matrix
Section titled “Decision Matrix”| Use Case | Recommended Format | CLI Command | Reason |
|---|---|---|---|
| AI agents | JSON | redlines file1 file2 |
Simplest invocation, structured output |
| CI/CD check | JSON | redlines file1 file2 or redlines json file1 file2 --quiet |
Parseable, scriptable, exit codes |
| Human review | Markdown (GHFM) | redlines markdown file1 file2 --markdown-style ghfm |
GitHub-compatible rendering |
| Terminal display | Rich text | redlines text file1 file2 |
Colored, formatted output |
| Metrics collection | Stats | redlines stats file1 file2 --quiet |
Log-friendly plain text |
| Automation/scripts | JSON + exit codes | redlines file1 file2 |
Exit code 0=changes, 1=no changes |
| HTML report | Markdown (none) | Programmatic API | Clean HTML tags for styling |
| Documentation | Markdown (streamlit) | Programmatic API | Streamlit-compatible markup |
Exit Code Usage
Section titled “Exit Code Usage”# Check if files differ (useful in CI/CD) - command-less invocationif redlines file1.txt file2.txt > /dev/null 2>&1; then echo "Exit code 0: Changes detected"else exitcode=$? if [ $exitcode -eq 1 ]; then echo "Exit code 1: No changes (files identical)" else echo "Exit code 2: Error occurred" fifi
# Or use the stats command for more verbose outputif redlines stats file1.txt file2.txt --quiet; then echo "Exit code 0: Changes detected"else echo "Exit code 1: No changes or error occurred"fiProgrammatic API
Section titled “Programmatic API”Core Classes
Section titled “Core Classes”Redlines Class
Section titled “Redlines Class”from redlines import Redlines
# Create instancediff = Redlines( source="original text", test="modified text", processor=None, # Optional: Custom processor (default: WholeDocumentProcessor) markdown_style="red_green" # Optional: Markdown style)
# Or compare laterdiff = Redlines("original text")result = diff.compare("modified text")Key Properties and Methods
Section titled “Key Properties and Methods”# Get changes (excludes "equal" operations)changes: list[Redline] = diff.changesredlines: list[Redline] = diff.redlines # Alias for changes
# Filter changes by operationinsertions = diff.get_changes(operation="insert")deletions = diff.get_changes(operation="delete")replacements = diff.get_changes(operation="replace")all_changes = diff.get_changes() # Same as .changes
# Get statisticsstats: Stats = diff.stats()
# Get opcodes (like difflib)opcodes: list[tuple] = diff.opcodes # [(operation, i1, i2, j1, j2), ...]
# Output formatsmarkdown: str = diff.output_markdownrich_text: Text = diff.output_richjson_str: str = diff.output_json(pretty=False)Redline Dataclass
Section titled “Redline Dataclass”from redlines.processor import Redline
# Structure of a Redline object@dataclassclass Redline: operation: Literal["delete", "insert", "replace"] source_text: str | None test_text: str | None source_position: tuple[int, int] | None # (start, end) token indices test_position: tuple[int, int] | None
# Example usagefor change in diff.changes: if change.operation == "replace": print(f"Line {change.source_position}: {change.source_text} → {change.test_text}")Stats Dataclass
Section titled “Stats Dataclass”from redlines.processor import Stats
# Structure of a Stats object@dataclassclass Stats: total_changes: int deletions: int insertions: int replacements: int longest_change_length: int shortest_change_length: int average_change_length: float change_ratio: float # 0.0 to 1.0 chars_added: int chars_deleted: int chars_net_change: int levenshtein_distance: int | None # None if library not installed
# Example usagestats = diff.stats()print(f"Modified {stats.change_ratio:.1%} of the document")print(f"Net change: {stats.chars_net_change:+d} characters")Error Handling
Section titled “Error Handling”Common Errors and Solutions
Section titled “Common Errors and Solutions”1. File Not Found (CLI)
Section titled “1. File Not Found (CLI)”$ redlines json nonexistent.txt other.txt# Error: Failed to read file 'nonexistent.txt': [Errno 2] No such file or directory
# Solution: Check file pathsif [ -f "file.txt" ]; then redlines json file.txt other.txtelse echo "File not found"fi2. Encoding Errors (CLI)
Section titled “2. Encoding Errors (CLI)”# Error: Failed to read file 'file.txt': File encoding is not UTF-8
# Solution: Convert file to UTF-8iconv -f ISO-8859-1 -t UTF-8 file.txt > file_utf8.txtredlines json file_utf8.txt other.txt3. Invalid Operation Filter
Section titled “3. Invalid Operation Filter”from redlines import Redlines
diff = Redlines(source, test)
try: changes = diff.get_changes(operation="invalid")except ValueError as e: print(f"Error: {e}") # Error: Invalid operation: invalid # Valid operations: "insert", "delete", "replace", or None4. Missing Optional Dependencies
Section titled “4. Missing Optional Dependencies”from redlines.processor import LEVENSHTEIN_AVAILABLE
if not LEVENSHTEIN_AVAILABLE: print("Levenshtein library not installed - distance will be None") # Install with: pip install redlines[levenshtein]
# Stats will still work, but levenshtein_distance will be Nonestats = diff.stats()if stats.levenshtein_distance is not None: print(f"Edit distance: {stats.levenshtein_distance}")5. Empty or Identical Files
Section titled “5. Empty or Identical Files”from redlines import Redlines
# Empty filesdiff = Redlines("", "")stats = diff.stats()assert stats.total_changes == 0assert stats.change_ratio == 0.0
# Identical filesdiff = Redlines("Hello world", "Hello world")stats = diff.stats()assert stats.total_changes == 0# CLI exit code will be 1 (no changes)Defensive Programming Pattern
Section titled “Defensive Programming Pattern”from pathlib import Pathfrom redlines import Redlinesimport json
def safe_compare_files(file1: str, file2: str) -> dict: """Safely compare two files with comprehensive error handling.""" try: # Validate files exist path1, path2 = Path(file1), Path(file2) if not path1.exists(): return {"error": f"File not found: {file1}"} if not path2.exists(): return {"error": f"File not found: {file2}"}
# Read files try: source = path1.read_text(encoding="utf-8") test = path2.read_text(encoding="utf-8") except UnicodeDecodeError as e: return {"error": f"Encoding error: {e}"}
# Compare diff = Redlines(source, test) stats = diff.stats()
return { "success": True, "file1": file1, "file2": file2, "total_changes": stats.total_changes, "change_ratio": stats.change_ratio, "has_changes": stats.total_changes > 0 }
except Exception as e: return {"error": f"Unexpected error: {e}"}
# Usageresult = safe_compare_files("old.txt", "new.txt")if "error" in result: print(f"Error: {result['error']}")else: print(f"Changes: {result['total_changes']}")Performance Guidelines
Section titled “Performance Guidelines”Processor Comparison
Section titled “Processor Comparison”| Processor | Speed | Use Case | Python Version |
|---|---|---|---|
| WholeDocumentProcessor (default) | Fast (5-6x faster) | Simple documents, speed critical | 3.10+ |
| NupunktProcessor | Slower | Legal/technical docs, sentence-level accuracy | 3.11+ |
Speed Benchmarks
Section titled “Speed Benchmarks”| File Size | WholeDocument | Nupunkt | Difference |
|---|---|---|---|
| 400 chars | 0.19 ms | 0.40 ms | 2.1x |
| 4 KB | 0.39 ms | 2.34 ms | 6.0x |
| 40 KB | 3.85 ms | 25.14 ms | 6.5x |
| 400 KB | 37.92 ms | 241.68 ms | 6.4x |
Throughput:
- WholeDocumentProcessor: ~10 million chars/second
- NupunktProcessor: ~1.6 million chars/second
When to Use Each Processor
Section titled “When to Use Each Processor”Use WholeDocumentProcessor (Default) When:
Section titled “Use WholeDocumentProcessor (Default) When:”- Processing large volumes of documents
- Speed is critical
- Documents are simple without complex punctuation
- Paragraph-level granularity is sufficient
Use NupunktProcessor When:
Section titled “Use NupunktProcessor When:”from redlines import Redlinesfrom redlines.processor import NupunktProcessor
processor = NupunktProcessor()diff = Redlines(source, test, processor=processor)- Working with legal or technical documents
- Need sentence-level granularity
- Documents contain many abbreviations (Dr., Mr., etc.)
- URLs, email addresses, or decimal numbers in text
- Performance overhead (2-6x) is acceptable
Sentence mode preserves the input’s paragraph boundaries (fixed in 0.6.2): sentences are anchored within their paragraph, so output is not reflowed one sentence per paragraph.
Performance Tips
Section titled “Performance Tips”- Reuse Redlines instance for multiple comparisons:
diff = Redlines(source)result1 = diff.compare(test1)result2 = diff.compare(test2) # Faster than creating new instance- Use CLI for one-off comparisons:
# CLI is optimized for single comparisonsredlines json file1.txt file2.txt- Batch processing pattern:
# Process multiple files efficientlyfrom concurrent.futures import ThreadPoolExecutor
def compare_file_pair(files): file1, file2 = files return Redlines( Path(file1).read_text(), Path(file2).read_text() ).stats()
with ThreadPoolExecutor(max_workers=4) as executor: results = executor.map(compare_file_pair, file_pairs)Integration Examples
Section titled “Integration Examples”Example 1: Pre-commit Hook
Section titled “Example 1: Pre-commit Hook”#!/bin/bash# Compare staged files with HEADfor file in $(git diff --cached --name-only --diff-filter=M); do if [ -f "$file" ]; then # Get old version git show HEAD:"$file" > /tmp/old_version
# Compare with new version if ! redlines stats /tmp/old_version "$file" --quiet > /dev/null; then echo "Error comparing $file" exit 1 fi
echo "✓ $file: Changes validated" rm /tmp/old_version fidone
exit 0Example 2: CI/CD Check
Section titled “Example 2: CI/CD Check”name: Check Documentation Changes
on: [pull_request]
jobs: check-docs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0
- name: Install redlines run: pip install redlines
- name: Check README changes run: | git show main:README.md > old_readme.md if redlines stats old_readme.md README.md --quiet; then echo "README has changes" redlines markdown old_readme.md README.md --markdown-style ghfm >> $GITHUB_STEP_SUMMARY else echo "README unchanged" fiExample 3: Batch Report Generator
Section titled “Example 3: Batch Report Generator”#!/usr/bin/env python3"""Generate HTML report comparing two directories."""
from pathlib import Pathfrom redlines import Redlinesimport json
def generate_report(dir1: Path, dir2: Path, output: Path): """Generate HTML diff report for all files in two directories."""
results = [] for file1 in sorted(dir1.glob("**/*.txt")): file2 = dir2 / file1.relative_to(dir1) if not file2.exists(): continue
diff = Redlines( file1.read_text(), file2.read_text(), markdown_style="none" ) stats = diff.stats()
if stats.total_changes > 0: results.append({ "file": str(file1.relative_to(dir1)), "stats": { "changes": stats.total_changes, "ratio": f"{stats.change_ratio:.1%}", "added": stats.chars_added, "deleted": stats.chars_deleted, }, "diff": diff.output_markdown })
# Generate HTML html = f"""<!DOCTYPE html><html><head> <title>Diff Report</title> <style> body {{ font-family: monospace; max-width: 1200px; margin: 0 auto; padding: 20px; }} .file {{ margin: 30px 0; border: 1px solid #ccc; padding: 20px; }} .stats {{ background: #f5f5f5; padding: 10px; margin-bottom: 20px; }} del {{ color: red; text-decoration: line-through; }} ins {{ color: green; text-decoration: underline; }} </style></head><body> <h1>Diff Report</h1> <p>Found {len(results)} files with changes</p>"""
for result in results: html += f""" <div class="file"> <h2>{result['file']}</h2> <div class="stats"> <strong>Changes:</strong> {result['stats']['changes']} | <strong>Ratio:</strong> {result['stats']['ratio']} | <strong>Added:</strong> {result['stats']['added']} chars | <strong>Deleted:</strong> {result['stats']['deleted']} chars </div> <div class="diff">{result['diff']}</div> </div>"""
html += """</body></html>"""
output.write_text(html) print(f"Report generated: {output}")
if __name__ == "__main__": import sys if len(sys.argv) != 4: print("Usage: generate_report.py <dir1> <dir2> <output.html>") sys.exit(1)
generate_report(Path(sys.argv[1]), Path(sys.argv[2]), Path(sys.argv[3]))Example 4: Test Suite Integration
Section titled “Example 4: Test Suite Integration”import pytestfrom redlines import Redlines
def test_function_preserves_behavior(): """Test that refactored code produces same output.""" # Original implementation output original_output = "Hello World"
# New implementation output new_output = my_refactored_function()
# Compare diff = Redlines(original_output, new_output) stats = diff.stats()
# Assert no changes assert stats.total_changes == 0, f"Output changed: {diff.output_markdown}"
def test_documentation_completeness(): """Ensure all API changes are documented.""" old_docs = Path("docs/api_v1.md").read_text() new_docs = Path("docs/api_v2.md").read_text()
diff = Redlines(old_docs, new_docs) stats = diff.stats()
# Ensure significant documentation updates assert stats.change_ratio > 0.05, "API changed but docs not updated enough"Example 5: Document Change Tracker
Section titled “Example 5: Document Change Tracker”#!/usr/bin/env python3"""Track document changes over time."""
import jsonfrom pathlib import Pathfrom datetime import datetimefrom redlines import Redlines
class DocumentTracker: def __init__(self, storage_dir: Path): self.storage_dir = storage_dir self.storage_dir.mkdir(exist_ok=True) self.history_file = storage_dir / "history.json" self.history = self._load_history()
def _load_history(self): if self.history_file.exists(): return json.loads(self.history_file.read_text()) return {"documents": {}}
def _save_history(self): self.history_file.write_text(json.dumps(self.history, indent=2))
def track_change(self, doc_name: str, content: str): """Track a change to a document.""" doc_history = self.history["documents"].setdefault(doc_name, { "versions": [], "total_changes": 0 })
# Get previous version versions = doc_history["versions"] if versions: prev_version = (self.storage_dir / versions[-1]["file"]).read_text()
# Compare diff = Redlines(prev_version, content) stats = diff.stats()
if stats.total_changes == 0: print(f"{doc_name}: No changes") return
# Record change version_file = f"{doc_name}_{len(versions)}.txt" (self.storage_dir / version_file).write_text(content)
versions.append({ "file": version_file, "timestamp": datetime.now().isoformat(), "changes": stats.total_changes, "change_ratio": stats.change_ratio, "chars_net_change": stats.chars_net_change })
doc_history["total_changes"] += stats.total_changes print(f"{doc_name}: {stats.total_changes} changes ({stats.change_ratio:.1%})") else: # First version version_file = f"{doc_name}_0.txt" (self.storage_dir / version_file).write_text(content) versions.append({ "file": version_file, "timestamp": datetime.now().isoformat(), "changes": 0, "change_ratio": 0.0, "chars_net_change": 0 }) print(f"{doc_name}: Initial version tracked")
self._save_history()
def get_diff(self, doc_name: str, version1: int, version2: int) -> str: """Get diff between two versions.""" versions = self.history["documents"][doc_name]["versions"]
content1 = (self.storage_dir / versions[version1]["file"]).read_text() content2 = (self.storage_dir / versions[version2]["file"]).read_text()
diff = Redlines(content1, content2) return diff.output_markdown
# Usagetracker = DocumentTracker(Path(".document_history"))tracker.track_change("README.md", Path("README.md").read_text())Additional Resources
Section titled “Additional Resources”- Full Documentation: this site
- GitHub Repository: https://github.com/houfu/redlines
- Example Scripts: examples/ directory
- Demo Project: redlines-textual
Quick Reference Card
Section titled “Quick Reference Card”# CLI - Command-less (recommended for agents)redlines "source" "test" # JSON outputredlines --pretty "source" "test" # Pretty JSONredlines file1.txt file2.txt # Works with files
# CLI - Traditional commands (for specific output formats)redlines text SOURCE TEST # Rich terminal displayredlines json SOURCE TEST --pretty # JSON with formattingredlines markdown SOURCE TEST -m ghfm # Markdown outputredlines stats SOURCE TEST --quiet # Statistics only# Python APIfrom redlines import Redlines
# Comparediff = Redlines(source, test)
# Get changesall_changes = diff.changesinsertions = diff.get_changes(operation="insert")
# Get statsstats = diff.stats()
# Outputmarkdown = diff.output_markdownjson_str = diff.output_json(pretty=True)rich_text = diff.output_richLast Updated: 2025-10-22 Version: 0.6.0+ Python: 3.10+