← All tasks
pythonclaude-code/python-t1 #33Not a task: already works

Text Diff Tool (python, written by Claude Code)

envgap__claude-code__python-t1-33

Written by a coding agent; not on GitHubWritten 2026-02-27

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
requirements.txt
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/python-t1 #33 · read the task the agent was given
Claude Code wrote this python project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: Text Diff Tool

Write a program that compares two text files and produces a detailed diff showing additions, deletions, and modifications with configurable output formats and context control.

FUNCTIONAL REQUIREMENTS:
- Accept two file paths as command-line arguments (original and modified)
- Compute the longest common subsequence (LCS) based diff to identify added, deleted, and changed lines
- Support multiple output formats via --format flag: unified diff (default, similar to git diff), side-by-side (two-column view), inline (changes marked within lines), and html (visual diff as an HTML page)
- Support configurable context lines around changes via --context flag (default: 3 lines of unchanged context around each change)
- Detect and highlight intra-line changes: when a line is modified, show exactly which words or characters changed within the line
- Support ignoring whitespace differences via --ignore-whitespace flag
- Support ignoring case differences via --ignore-case flag
- Support ignoring blank lines via --ignore-blank-lines flag
- Compute and display diff statistics: total lines in each file, lines added, lines deleted, lines modified, and a similarity percentage
- Support comparing directories via --recursive flag: compare all matching files in two directory trees and report which files are added, deleted, modified, or identical
- Apply color coding in console output: green for additions, red for deletions, yellow for modifications
- Save the diff output to a file via --output flag
- If no input files are given, generate two sample text files (original and modified version with insertions, deletions, modifications, and moved blocks), then compute and display the diff in all supported formats
- Handle errors: binary files (detect and skip with warning), missing files, encoding mismatches, and very large files

Create a complete Python project for a clean Ubuntu 22.04 machine with only Python 3.10+ installed. Include:
- Source code
- requirements.txt with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

diff_tool.py
"""
Text Diff Tool - Compares text files showing additions, deletions, and modifications.
Supports unified, side-by-side, and HTML output formats.
Uses difflib (built-in) and rich for terminal rendering.
"""

import argparse
import difflib
import html
import os
import sys
from pathlib import Path

from rich.console import Console
from rich.table import Table
from rich.text import Text
from rich.panel import Panel
from rich.syntax import Syntax


console = Console()


class TextDiffTool:
    """Compares two text files and displays differences in multiple formats."""

    def __init__(self, file1_path: str, file2_path: str, context_lines: int = 3):
        self.file1_path = file1_path
        self.file2_path = file2_path
        self.context_lines = context_lines
        self.file1_lines = self._read_file(file1_path)
        self.file2_lines = self._read_file(file2_path)

    @staticmethod
    def _read_file(filepath: str) -> list[str]:
        """Read file and return list of lines."""
        path = Path(filepath)
        if not path.exists():
            console.print(f"[red]Error:[/red] File not found: {filepath}")
            sys.exit(1)
        try:
            with open(path, "r", encoding="utf-8") as f:
                return f.readlines()
        except UnicodeDecodeError:
            console.print(f"[red]Error:[/red] Cannot read binary file: {filepath}")
            sys.exit(1)

    def get_unified_diff(self) -> list[str]:
        """Generate unified diff output."""
        return list(
            difflib.unified_diff(
                self.file1_lines,
                self.file2_lines,
                fromfile=self.file1_path,
                tofile=self.file2_path,
                n=self.context_lines,
            )
        )

    def get_context_diff(self) -> list[str]:
        """Generate context diff output."""
        return list(
            difflib.context_diff(
                self.file1_lines,
                self.file2_lines,
                fromfile=self.file1_path,
                tofile=self.file2_path,
                n=self.context_lines,
            )
        )

    def get_ndiff(self) -> list[str]:
        """Generate ndiff (detailed character-level) output."""
        return list(difflib.ndiff(self.file1_lines, self.file2_lines))

    def compute_stats(self) -> dict:
        """Compute diff statistics: additions, deletions, modifications."""
        matcher = difflib.SequenceMatcher(None, self.file1_lines, self.file2_lines)
        additions = 0
        deletions = 0
        modifications = 0

        for tag, i1, i2, j1, j2 in matcher.get_opcodes():
            if tag == "insert":
                additions += j2 - j1
            elif tag == "delete":
                deletions += i2 - i1
            elif tag == "replace":
                modifications += max(i2 - i1, j2 - j1)

        return {
            "additions": additions,
            "deletions": deletions,
            "modifications": modifications,
            "total_changes": additions + deletions + modifications,
            "file1_lines": len(self.file1_lines),
            "file2_lines": len(self.file2_lines),
        }

    def display_unified(self) -> None:
        """Display unified diff with rich formatting."""
        diff_lines = self.get_unified_diff()
        if not diff_lines:
            console.print("[green]Files are identical.[/green]")
            return

        output = Text()
        for line in diff_lines:
            line_stripped = line.rstrip("\n")
            if line.startswith("+++") or line.startswith("---"):
                output.append(line_stripped + "\n", style="bold white")
            elif line.startswith("@@"):
                output.append(line_stripped + "\n", style="cyan")
            elif line.startswith("+"):
                output.append(line_stripped + "\n", style="green")
            elif line.startswith("-"):
                output.append(line_stripped + "\n", style="red")
            else:
                output.append(line_stripped + "\n")

        console.print(
            Panel(output, title="Unified Diff", border_style="blue", expand=True)
        )

    def display_side_by_side(self, width: int = 80) -> None:
        """Display side-by-side diff using a rich table."""
        matcher = difflib.SequenceMatcher(None, self.file1_lines, self.file2_lines)
        table = Table(
            title="Side-by-Side Diff",
            show_header=True,
            header_style="bold magenta",
            expand=True,
        )
        table.add_column("Line", style="dim", width=5, justify="right")
        table.add_column(
            os.path.basename(self.file1_path), width=width // 2, overflow="fold"
        )
        table.add_column("Line", style="dim", width=5, justify="right")
        table.add_column(
            os.path.basename(self.file2_path), width=width // 2, overflow="fold"
        )

        for tag, i1, i2, j1, j2 in matcher.get_opcodes():
            if tag == "equal":
                for i, j in zip(range(i1, i2), range(j1, j2)):
                    left = self.file1_lines[i].rstrip("\n")
                    right = self.file2_lines[j].rstrip("\n")
                    table.add_row(str(i + 1), left, str(j + 1), right)
            elif tag == "replace":
                max_len = max(i2 - i1, j2 - j1)
                for k in range(max_len):
                    left_num = str(i1 + k + 1) if k < (i2 - i1) else ""
                    right_num = str(j1 + k + 1) if k < (j2 - j1) else ""
                    left = (
                        Text(
                            self.file1_lines[i1 + k].rstrip("\n"), style="red strike"
                        )
                        if k < (i2 - i1)
                        else Text("")
                    )
                    right = (
                        Text(self.file2_lines[j1 + k].rstrip("\n"), style="green")
                        if k < (j2 - j1)
                        else Text("")
                    )
                    table.add_row(left_num, left, right_num, right)
            elif tag == "delete":
                for i in range(i1, i2):
                    left = Text(self.file1_lines[i].rstrip("\n"), style="red strike")
                    table.add_row(str(i + 1), left, "", Text(""))
            elif tag == "insert":
                for j in range(j1, j2):
                    right = Text(self.file2_lines[j].rstrip("\n"), style="green")
                    table.add_row("", Text(""), str(j + 1), right)

        console.print(table)

    def generate_html(self, output_path: str = None) -> str:
        """Generate HTML diff report."""
        differ = difflib.HtmlDiff(tabsize=4, wrapcolumn=80)
        html_content = differ.make_file(
            self.file1_lines,
            self.file2_lines,
            fromdesc=self.file1_path,
            todesc=self.file2_path,
            context=True,
            numlines=self.context_lines,
        )

        # Wrap with additional styling
        stats = self.compute_stats()
        stats_html = f"""
        <div style="font-family: monospace; padding: 10px; background: #f0f0f0;
                     border: 1px solid #ccc; margin: 10px 0;">
            <h3>Diff Statistics</h3>
            <p>Additions: <span style="color: green;">{stats['additions']}</span></p>
            <p>Deletions: <span style="color: red;">{stats['deletions']}</span></p>
            <p>Modifications: <span style="color: orange;">{stats['modifications']}</span></p>
            <p>Total changes: {stats['total_changes']}</p>
            <p>File 1 lines: {stats['file1_lines']} | File 2 lines: {stats['file2_lines']}</p>
        </div>
        """
        html_content = html_content.replace("</body>", stats_html + "</body>")

        if output_path:
            with open(output_path, "w", encoding="utf-8") as f:
                f.write(html_content)
            console.print(f"[green]HTML diff saved to: {output_path}[/green]")

        return html_content

    def display_stats(self) -> None:
        """Display diff statistics with rich formatting."""
        stats = self.compute_stats()
        table = Table(title="Diff Statistics", show_header=True)
        table.add_column("Metric", style="bold")
        table.add_column("Value", justify="right")

        table.add_row("Additions", f"[green]+{stats['additions']}[/green]")
        table.add_row("Deletions", f"[red]-{stats['deletions']}[/red]")
        table.add_row("Modifications", f"[yellow]~{stats['modifications']}[/yellow]")
        table.add_row("Total Changes", str(stats["total_changes"]))
        table.add_row("File 1 Lines", str(stats["file1_lines"]))
        table.add_row("File 2 Lines", str(stats["file2_lines"]))

        console.print(table)


def main():
    parser = argparse.ArgumentParser(
        description="Text Diff Tool - Compare text files with rich output"
    )
    parser.add_argument("file1", help="Path to the first (original) file")
    parser.add_argument("file2", help="Path to the second (modified) file")
    parser.add_argument(
        "--format",
        choices=["unified", "side-by-side", "html", "ndiff", "context", "all"],
        default="unified",
        help="Output format (default: unified)",
    )
    parser.add_argument(
        "--context-lines",
        type=int,
        default=3,
        help="Number of context lines (default: 3)",
    )
    parser.add_argument(
        "--html-output", type=str, default=None, help="Path for HTML output file"
    )
    parser.add_argument(
        "--width",
        type=int,
        default=80,
        help="Column width for side-by-side view (default: 80)",
    )
    parser.add_argument(
        "--stats", action="store_true", help="Show diff statistics"
    )

    args = parser.parse_args()

    diff_tool = TextDiffTool(args.file1, args.file2, args.context_lines)

    if args.stats or args.format == "all":
        diff_tool.display_stats()

    if args.format == "unified" or args.format == "all":
        diff_tool.display_unified()
    if args.format == "side-by-side" or args.format == "all":
        diff_tool.display_side_by_side(args.width)
    if args.format == "html" or args.format == "all":
        output_path = args.html_output or "diff_report.html"
        diff_tool.generate_html(output_path)
    if args.format == "ndiff":
        lines = diff_tool.get_ndiff()
        for line in lines:
            console.print(line.rstrip("\n"))
    if args.format == "context":
        lines = diff_tool.get_context_diff()
        for line in lines:
            console.print(line.rstrip("\n"))


if __name__ == "__main__":
    main()
README.md
# Text Diff Tool (Python - Trial 1)

A text file comparison tool that shows additions, deletions, and modifications in unified, side-by-side, and HTML formats.

## Dependencies

- **difflib** (built-in): Python standard library module for computing deltas between sequences
- **rich** (13.7.0): Rich text formatting and terminal rendering library

## Installation

```bash
pip install -r requirements.txt
```

## Usage

```bash
# Unified diff (default)
python diff_tool.py file1.txt file2.txt

# Side-by-side diff
python diff_tool.py file1.txt file2.txt --format side-by-side

# HTML output
python diff_tool.py file1.txt file2.txt --format html --html-output report.html

# All formats with statistics
python diff_tool.py file1.txt file2.txt --format all --stats

# Context diff
python diff_tool.py file1.txt file2.txt --format context --context-lines 5
```

## Features

- Unified diff display with color-coded additions/deletions
- Side-by-side comparison in a formatted table
- HTML report generation with embedded statistics
- Detailed character-level diff (ndiff format)
- Context diff format
- Diff statistics (additions, deletions, modifications)
- Configurable context lines
requirements.txt
rich==13.7.0