diff --git a/README.md b/README.md index 8f04e79..858d179 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Just show me the code. -Tired of clicking around complex file hierarchies of GitHub repos? Do you just want to see all of the code on a single page? Enter `rendergit`. Flatten any GitHub repository into a single, static HTML page with syntax highlighting, markdown rendering, and a clean sidebar navigation. Perfect for code review, exploration, and an instant Ctrl+F experience. +Tired of clicking around complex file hierarchies of GitHub repos? Do you just want to see all of the code on a single page? Enter `rendergit`. Flatten any GitHub repository or local directory into a single, static HTML page with syntax highlighting, markdown rendering, and a clean sidebar navigation. Perfect for code review, exploration, and an instant Ctrl+F experience. ## Basic usage @@ -10,7 +10,8 @@ Install and use easily with [uv](https://docs.astral.sh/uv/): ```bash uv tool install git+https://github.com/karpathy/rendergit -rendergit https://github.com/karpathy/nanogpt +rendergit https://github.com/karpathy/nanogpt # Remote repository +rendergit /path/to/local/repo # Local repository ``` Alternatively, more manual pip install example: @@ -19,11 +20,12 @@ Alternatively, more manual pip install example: git clone https://github.com/karpathy/rendergit cd rendergit pip install -e . -rendergit https://github.com/karpathy/nanoGPT +rendergit https://github.com/karpathy/nanoGPT # Remote repository +rendergit /path/to/local/repo # Local repository ``` The code will: -1. Clone the repo to a temporary directory +1. Clone the repo (if URL) or use the local directory (if path) 2. Render its source code into a single static temporary HTML file 3. Automatically open the file in your browser @@ -35,6 +37,7 @@ There's a few other smaller options, see the code. ## Features +- **Local & Remote Repository Support** - works with GitHub URLs and local directories - **Dual view modes** - toggle between Human and LLM views - **👤 Human View**: Pretty interface with syntax highlighting and navigation - **🤖 LLM View**: Raw CXML text format - perfect for copying to Claude/ChatGPT for code analysis diff --git a/pyproject.toml b/pyproject.toml index 584fdf4..5f56cec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "rendergit" version = "0.1.0" -description = "Flatten a GitHub repo into a single static HTML page for fast skimming and Ctrl+F" +description = "Flatten a GitHub repo or local directory into a single static HTML page for fast skimming and Ctrl+F" readme = "README.md" license = "0BSD" authors = [ diff --git a/rendergit.py b/rendergit.py index 03e05f5..0145fba 100644 --- a/rendergit.py +++ b/rendergit.py @@ -471,8 +471,8 @@ def derive_temp_output_path(repo_url: str) -> pathlib.Path: def main() -> int: - ap = argparse.ArgumentParser(description="Flatten a GitHub repo to a single HTML page") - ap.add_argument("repo_url", help="GitHub repo URL (https://github.com/owner/repo[.git])") + ap = argparse.ArgumentParser(description="Flatten a GitHub repo or local directory to a single HTML page") + ap.add_argument("repo_source", help="GitHub repo URL or local directory path") ap.add_argument("-o", "--out", help="Output HTML file path (default: temporary file derived from repo name)") ap.add_argument("--max-bytes", type=int, default=MAX_DEFAULT_BYTES, help="Max file size to render (bytes); larger files are listed but skipped") ap.add_argument("--no-open", action="store_true", help="Don't open the HTML file in browser after generation") @@ -480,17 +480,31 @@ def main() -> int: # Set default output path if not provided if args.out is None: - args.out = str(derive_temp_output_path(args.repo_url)) + args.out = str(derive_temp_output_path(args.repo_source)) - tmpdir = tempfile.mkdtemp(prefix="flatten_repo_") - repo_dir = pathlib.Path(tmpdir, "repo") + source_path = pathlib.Path(args.repo_source) + is_local_repo = source_path.is_dir() + tmpdir = None # Initialize tmpdir to None - try: - print(f"📁 Cloning {args.repo_url} to temporary directory: {repo_dir}", file=sys.stderr) - git_clone(args.repo_url, str(repo_dir)) + if is_local_repo: + repo_dir = source_path.resolve() + # For a local repo, use the directory name as the "URL" in the report + repo_url = repo_dir.name + print(f"📁 Using local repository: {repo_dir}", file=sys.stderr) + head = git_head_commit(str(repo_dir)) + print(f"✓ Local repo identified (HEAD: {head[:8]})", file=sys.stderr) + else: + # If not a local dir, assume it's a URL to be cloned + repo_url = args.repo_source + tmpdir = tempfile.mkdtemp(prefix="flatten_repo_") + repo_dir = pathlib.Path(tmpdir, "repo") + print(f"📁 Cloning {repo_url} to temporary directory: {repo_dir}", file=sys.stderr) + git_clone(repo_url, str(repo_dir)) head = git_head_commit(str(repo_dir)) print(f"✓ Clone complete (HEAD: {head[:8]})", file=sys.stderr) + try: + print(f"📊 Scanning files in {repo_dir}...", file=sys.stderr) infos = collect_files(repo_dir, args.max_bytes) rendered_count = sum(1 for i in infos if i.decision.include) @@ -498,7 +512,7 @@ def main() -> int: print(f"✓ Found {len(infos)} files total ({rendered_count} will be rendered, {skipped_count} skipped)", file=sys.stderr) print(f"🔨 Generating HTML...", file=sys.stderr) - html_out = build_html(args.repo_url, repo_dir, head, infos) + html_out = build_html(repo_url, repo_dir, head, infos) out_path = pathlib.Path(args.out) print(f"💾 Writing HTML file: {out_path.resolve()}", file=sys.stderr) @@ -510,10 +524,11 @@ def main() -> int: print(f"🌐 Opening {out_path} in browser...", file=sys.stderr) webbrowser.open(f"file://{out_path.resolve()}") - print(f"🗑️ Cleaning up temporary directory: {tmpdir}", file=sys.stderr) return 0 finally: - shutil.rmtree(tmpdir, ignore_errors=True) + if tmpdir: + print(f"🗑️ Cleaning up temporary directory: {tmpdir}", file=sys.stderr) + shutil.rmtree(tmpdir, ignore_errors=True) if __name__ == "__main__":